Merge branch 'master' into sb/opt-filename

* master: (654 commits)
  http-push.c::remove_locks(): fix use after free
  t/t3400-rebase.sh: add more tests to help migrating git-rebase.sh to C
  post-receive-email: hooks.showrev: show how to include both web link and patch
  MinGW: Fix compiler warning in merge-recursive
  MinGW: Add a simple getpass()
  MinGW: use POSIX signature of waitpid()
  MinGW: the path separator to split GITPERLLIB is ';' on Win32
  MinGW: Scan for \r in addition to \n when reading shbang lines
  gitweb: Sanitize title attribute in format_subject_html
  Terminate argv with NULL before calling setup_revisions()
  doc/git-rebase.txt: remove mention of multiple strategies
  git-send-email: Handle quotes when parsing .mailrc files
  git-svn: add --authors-prog option
  git-svn: Set svn.authorsfile if it is passed to git svn clone
  git-svn: Correctly report max revision when following deleted paths
  git-svn: Fix for svn paths removed > log-window-size revisions ago
  git-svn testsuite: use standard configuration for Subversion tools
  grep: fix word-regexp colouring
  completion: use git rev-parse to detect bare repos
  Cope better with a _lot_ of packs
  ...
This commit is contained in:
Junio C Hamano 2009-05-25 00:59:07 -07:00
Родитель 4c8d4c14c6 9619ff1415
Коммит ee969693c5
448 изменённых файлов: 16245 добавлений и 6362 удалений

4
.gitignore поставляемый
Просмотреть файл

@ -11,6 +11,7 @@ git-apply
git-archimport git-archimport
git-archive git-archive
git-bisect git-bisect
git-bisect--helper
git-blame git-blame
git-branch git-branch
git-bundle git-bundle
@ -35,6 +36,8 @@ git-diff
git-diff-files git-diff-files
git-diff-index git-diff-index
git-diff-tree git-diff-tree
git-difftool
git-difftool--helper
git-describe git-describe
git-fast-export git-fast-export
git-fast-import git-fast-import
@ -78,6 +81,7 @@ git-merge-recursive
git-merge-resolve git-merge-resolve
git-merge-subtree git-merge-subtree
git-mergetool git-mergetool
git-mergetool--lib
git-mktag git-mktag
git-mktree git-mktree
git-name-rev git-name-rev

Просмотреть файл

@ -129,3 +129,6 @@ For C programs:
used in the git core command set (unless your command is clearly used in the git core command set (unless your command is clearly
separate from it, such as an importer to convert random-scm-X separate from it, such as an importer to convert random-scm-X
repositories to git). repositories to git).
- When we pass <string, length> pair to functions, we should try to
pass them in that order.

Просмотреть файл

@ -41,7 +41,8 @@ man7dir=$(mandir)/man7
ASCIIDOC=asciidoc ASCIIDOC=asciidoc
ASCIIDOC_EXTRA = ASCIIDOC_EXTRA =
MANPAGE_XSL = callouts.xsl MANPAGE_XSL = manpage-normal.xsl
XMLTO_EXTRA =
INSTALL?=install INSTALL?=install
RM ?= rm -f RM ?= rm -f
DOC_REF = origin/man DOC_REF = origin/man
@ -59,13 +60,52 @@ endif
-include ../config.mak.autogen -include ../config.mak.autogen
-include ../config.mak -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 ifdef ASCIIDOC8
ASCIIDOC_EXTRA += -a asciidoc7compatible ASCIIDOC_EXTRA += -a asciidoc7compatible
endif endif
ifdef DOCBOOK_XSL_172 ifdef DOCBOOK_XSL_172
ASCIIDOC_EXTRA += -a docbook-xsl-172 ASCIIDOC_EXTRA += -a git-asciidoc-no-roff
MANPAGE_XSL = manpage-1.72.xsl 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 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
SHELL_PATH ?= $(SHELL)
# Shell quote;
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
# #
# Please note that there is a minor bug in asciidoc. # Please note that there is a minor bug in asciidoc.
@ -76,6 +116,32 @@ endif
# yourself - yes, all 6 characters of it! # 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 all: html man
html: $(DOC_HTML) html: $(DOC_HTML)
@ -116,10 +182,10 @@ install-pdf: pdf
$(INSTALL) -m 644 user-manual.pdf $(DESTDIR)$(pdfdir) $(INSTALL) -m 644 user-manual.pdf $(DESTDIR)$(pdfdir)
install-html: html install-html: html
sh ./install-webdoc.sh $(DESTDIR)$(htmldir) '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir)
../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE ../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
$(MAKE) -C ../ GIT-VERSION-FILE $(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) GIT-VERSION-FILE
-include ../GIT-VERSION-FILE -include ../GIT-VERSION-FILE
@ -127,8 +193,8 @@ install-html: html
# Determine "include::" file references in asciidoc files. # Determine "include::" file references in asciidoc files.
# #
doc.dep : $(wildcard *.txt) build-docdep.perl doc.dep : $(wildcard *.txt) build-docdep.perl
$(RM) $@+ $@ $(QUIET_GEN)$(RM) $@+ $@ && \
$(PERL_PATH) ./build-docdep.perl >$@+ $(PERL_PATH) ./build-docdep.perl >$@+ $(QUIET_STDERR) && \
mv $@+ $@ mv $@+ $@
-include doc.dep -include doc.dep
@ -146,102 +212,105 @@ cmds_txt = cmds-ancillaryinterrogators.txt \
$(cmds_txt): cmd-list.made $(cmds_txt): cmd-list.made
cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT) cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT)
$(RM) $@ $(QUIET_GEN)$(RM) $@ && \
$(PERL_PATH) ./cmd-list.perl ../command-list.txt $(PERL_PATH) ./cmd-list.perl ../command-list.txt $(QUIET_STDERR) && \
date >$@ date >$@
clean: clean:
$(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 $(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) howto-index.txt howto/*.html doc.dep
$(RM) technical/api-*.html technical/api-index.txt $(RM) technical/api-*.html technical/api-index.txt
$(RM) $(cmds_txt) *.made $(RM) $(cmds_txt) *.made
$(MAN_HTML): %.html : %.txt $(MAN_HTML): %.html : %.txt
$(RM) $@+ $@ $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
$(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf \ $(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf \
$(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< && \
mv $@+ $@ mv $@+ $@
%.1 %.5 %.7 : %.xml %.1 %.5 %.7 : %.xml
$(RM) $@ $(QUIET_XMLTO)$(RM) $@ && \
xmlto -m $(MANPAGE_XSL) man $< xmlto -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
%.xml : %.txt %.xml : %.txt
$(RM) $@+ $@ $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
$(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \ $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \
$(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< && \
mv $@+ $@ mv $@+ $@
user-manual.xml: user-manual.txt user-manual.conf user-manual.xml: user-manual.txt user-manual.conf
$(ASCIIDOC) -b docbook -d book $< $(QUIET_ASCIIDOC)$(ASCIIDOC) $(ASCIIDOC_EXTRA) -b docbook -d book $<
technical/api-index.txt: technical/api-index-skel.txt \ technical/api-index.txt: technical/api-index-skel.txt \
technical/api-index.sh $(patsubst %,%.txt,$(API_DOCS)) technical/api-index.sh $(patsubst %,%.txt,$(API_DOCS))
cd technical && sh ./api-index.sh $(QUIET_GEN)cd technical && '$(SHELL_PATH_SQ)' ./api-index.sh
$(patsubst %,%.html,$(API_DOCS) technical/api-index): %.html : %.txt $(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 $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) $*.txt
XSLT = docbook.xsl XSLT = docbook.xsl
XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
user-manual.html: user-manual.xml user-manual.html: user-manual.xml
xsltproc $(XSLTOPTS) -o $@ $(XSLT) $< $(QUIET_XSLTPROC)xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
git.info: user-manual.texi 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 user-manual.texi: user-manual.xml
$(RM) $@+ $@ $(QUIET_DB2TEXI)$(RM) $@+ $@ && \
$(DOCBOOK2X_TEXI) user-manual.xml --encoding=UTF-8 --to-stdout | \ $(DOCBOOK2X_TEXI) user-manual.xml --encoding=UTF-8 --to-stdout >$@++ && \
$(PERL_PATH) fix-texi.perl >$@+ $(PERL_PATH) fix-texi.perl <$@++ >$@+ && \
rm $@++ && \
mv $@+ $@ mv $@+ $@
user-manual.pdf: user-manual.xml user-manual.pdf: user-manual.xml
$(RM) $@+ $@ $(QUIET_DBLATEX)$(RM) $@+ $@ && \
$(DBLATEX) -o $@+ -p /etc/asciidoc/dblatex/asciidoc-dblatex.xsl -s /etc/asciidoc/dblatex/asciidoc-dblatex.sty $< $(DBLATEX) -o $@+ -p /etc/asciidoc/dblatex/asciidoc-dblatex.xsl -s /etc/asciidoc/dblatex/asciidoc-dblatex.sty $< && \
mv $@+ $@ mv $@+ $@
gitman.texi: $(MAN_XML) cat-texi.perl gitman.texi: $(MAN_XML) cat-texi.perl
$(RM) $@+ $@ $(QUIET_DB2TEXI)$(RM) $@+ $@ && \
($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --encoding=UTF-8 \ ($(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 $@+ $@ mv $@+ $@
gitman.info: gitman.texi 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 $(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml
$(RM) $@+ $@ $(QUIET_DB2TEXI)$(RM) $@+ $@ && \
$(DOCBOOK2X_TEXI) --to-stdout $*.xml >$@+ $(DOCBOOK2X_TEXI) --to-stdout $*.xml >$@+ && \
mv $@+ $@ mv $@+ $@
howto-index.txt: howto-index.sh $(wildcard howto/*.txt) howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
$(RM) $@+ $@ $(QUIET_GEN)$(RM) $@+ $@ && \
sh ./howto-index.sh $(wildcard howto/*.txt) >$@+ '$(SHELL_PATH_SQ)' ./howto-index.sh $(wildcard howto/*.txt) >$@+ && \
mv $@+ $@ mv $@+ $@
$(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt $(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt
$(ASCIIDOC) -b xhtml11 $*.txt $(QUIET_ASCIIDOC)$(ASCIIDOC) $(ASCIIDOC_EXTRA) -b xhtml11 $*.txt
WEBDOC_DEST = /pub/software/scm/git/docs WEBDOC_DEST = /pub/software/scm/git/docs
$(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt $(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
$(RM) $@+ $@ $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
sed -e '1,/^$$/d' $< | $(ASCIIDOC) -b xhtml11 - >$@+ sed -e '1,/^$$/d' $< | $(ASCIIDOC) $(ASCIIDOC_EXTRA) -b xhtml11 - >$@+ && \
mv $@+ $@ mv $@+ $@
install-webdoc : html install-webdoc : html
sh ./install-webdoc.sh $(WEBDOC_DEST) '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(WEBDOC_DEST)
quick-install: quick-install-man quick-install: quick-install-man
quick-install-man: quick-install-man:
sh ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir) '$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir)
quick-install-html: quick-install-html:
sh ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir) '$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir)
.PHONY: .FORCE-GIT-VERSION-FILE .PHONY: .FORCE-GIT-VERSION-FILE

Просмотреть файл

@ -0,0 +1,10 @@
GIT v1.6.3.1 Release Notes
==========================
Fixes since v1.6.3
------------------
* "git checkout -b new-branch" with a staged change in the index
incorrectly primed the in-index cache-tree, resulting a wrong tree
object to be written out of the index. This is a grave regression
since the last 1.6.2.X maintenance release.

Просмотреть файл

@ -0,0 +1,182 @@
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)
* various git-svn updates.
* git-gui updates, including an update to Russian translation, and a
fix to an infinite loop when showing an empty diff.
* gitk updates, including an update to Russian translation and improved Windows
support.
(performance)
* many uses of lstat(2) in the codepath for "git checkout" have been
optimized out.
(usability, bells and whistles)
* Boolean configuration variable yes/no can be written as on/off.
* 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".
* "--graph" to the "git log" family can draw the commit ancestry graph
in colors.
* 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.
* @{-1} is a new way to refer to the last branch you were on introduced in
1.6.2, but the initial implementation did not teach this to a few
commands. Now the syntax works with "branch -m @{-1} newname".
* git-archive learned --output=<file> option.
* git-archive takes attributes from the tree being archived; strictly
speaking, this is an incompatible behaviour change, but is a good one.
Use --worktree-attributes option to allow it to read attributes from
the work tree as before (deprecated git-tar tree command always reads
attributes from the work tree).
* 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-branch -v -v" is a new way to get list of names for branches and the
"upstream" branch for them.
* 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-difftool is now part of the officially supported command, primarily
maintained by David Aguilar.
* git-for-each-ref learned a new "upstream" token.
* 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 can be told to always add sign-off with a configuration
variable.
* 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-format-patch gives human readable names to the attached files, when
told to send patches as attachments.
* 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 remote update --prune $remote" updates from the named remote and
then prunes stale tracking branches.
* 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.
* Support for building and testing a subset of git on a system without a
working perl has been improved.
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.
* "git-apply" rejected a patch that swaps two files (i.e. renames A to B
and B to A at the same time). May need to be backported by cherry
picking d8c81df and then 7fac0ee).
* 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).

Просмотреть файл

@ -0,0 +1,59 @@
GIT v1.6.4 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.3
--------------------
(subsystems)
(performance)
(usability, bells and whistles)
(developers)
Fixes since v1.6.3
------------------
All of the fixes in v1.6.3.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.3.X series.
---
exec >/var/tmp/1
echo O=$(git describe master)
O=v1.6.3
git shortlog --no-merges $O..master ^maint

Просмотреть файл

@ -6,9 +6,13 @@ Checklist (and a short version for the impatient):
- check for unnecessary whitespace with "git diff --check" - check for unnecessary whitespace with "git diff --check"
before committing before committing
- do not check in commented out code or unneeded files - do not check in commented out code or unneeded files
- provide a meaningful commit message
- the first line of the commit message should be a short - the first line of the commit message should be a short
description and should skip the full stop description and should skip the full stop
- the body should provide a meaningful commit message, which:
- uses the imperative, present tense: "change",
not "changed" or "changes".
- includes motivation for the change, and contrasts
its implementation with previous behaviour
- if you want your work included in git.git, add a - if you want your work included in git.git, add a
"Signed-off-by: Your Name <you@example.com>" line to the "Signed-off-by: Your Name <you@example.com>" line to the
commit message (or just use the option "-s" when commit message (or just use the option "-s" when
@ -62,6 +66,14 @@ Describe the technical detail of the change(s).
If your description starts to get too long, that's a sign that you If your description starts to get too long, that's a sign that you
probably need to split up your commit to finer grained pieces. probably need to split up your commit to finer grained pieces.
That being said, patches which plainly describe the things that
help reviewers check the patch, and future maintainers understand
the code, are the most beautiful patches. Descriptions that summarise
the point in the subject well, and describe the motivation for the
change, the approach taken by the change, and if relevant how this
differs substantially from the prior version, can be found on Usenet
archives back into the late 80's. Consider it like good Netiquette,
but for code.
Oh, another thing. I am picky about whitespaces. Make sure your Oh, another thing. I am picky about whitespaces. Make sure your
changes do not trigger errors with the sample pre-commit hook shipped changes do not trigger errors with the sample pre-commit hook shipped
@ -491,6 +503,12 @@ message, complete the addressing and subject fields, and press send.
Gmail 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 Submitting properly formatted patches via Gmail is simple now that
IMAP support is available. First, edit your ~/.gitconfig to specify your IMAP support is available. First, edit your ~/.gitconfig to specify your
account settings: account settings:
@ -503,6 +521,9 @@ account settings:
port = 993 port = 993
sslverify = false 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 Next, ensure that your Gmail settings are correct. In "Settings" the
"Use Unicode (UTF-8) encoding for outgoing messages" should be checked. "Use Unicode (UTF-8) encoding for outgoing messages" should be checked.
@ -513,3 +534,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 Go to your Gmail account, open the Drafts folder, find the patch email, fill
in the To: and CC: fields and send away! in the To: and CC: fields and send away!

Просмотреть файл

@ -27,7 +27,7 @@ ifdef::backend-docbook[]
endif::backend-docbook[] endif::backend-docbook[]
ifdef::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. # "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. # v1.72 breaks with this because it replaces dots not in roff requests.
[listingblock] [listingblock]
@ -42,16 +42,16 @@ ifdef::doctype-manpage[]
endif::doctype-manpage[] endif::doctype-manpage[]
</literallayout> </literallayout>
{title#}</example> {title#}</example>
endif::docbook-xsl-172[] endif::git-asciidoc-no-roff[]
ifdef::docbook-xsl-172[] ifdef::git-asciidoc-no-roff[]
ifdef::doctype-manpage[] ifdef::doctype-manpage[]
# The following two small workarounds insert a simple paragraph after screen # The following two small workarounds insert a simple paragraph after screen
[listingblock] [listingblock]
<example><title>{title}</title> <example><title>{title}</title>
<screen> <literallayout>
| |
</screen><simpara></simpara> </literallayout><simpara></simpara>
{title#}</example> {title#}</example>
[verseblock] [verseblock]
@ -59,10 +59,11 @@ ifdef::doctype-manpage[]
{title%}<literallayout{id? id="{id}"}> {title%}<literallayout{id? id="{id}"}>
{title#}<literallayout> {title#}<literallayout>
| |
</literallayout><simpara></simpara> </literallayout>
{title#}</para></formalpara> {title#}</para></formalpara>
{title%}<simpara></simpara>
endif::doctype-manpage[] endif::doctype-manpage[]
endif::docbook-xsl-172[] endif::git-asciidoc-no-roff[]
endif::backend-docbook[] endif::backend-docbook[]
ifdef::doctype-manpage[] ifdef::doctype-manpage[]

Просмотреть файл

@ -70,6 +70,14 @@ of lines before or after the line given by <start>.
tree copy has the contents of the named file (specify tree copy has the contents of the named file (specify
`-` to make the command read from the standard input). `-` 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>|:: -M|<num>|::
Detect moving lines in the file as well. When a commit Detect moving lines in the file as well. When a commit
moves a block of lines in a file (e.g. the original file moves a block of lines in a file (e.g. the original file

Просмотреть файл

@ -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&#10;</xsl:text>
<xsl:apply-templates/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="callout">
<xsl:value-of select="concat('\fB',substring-after(@arearefs,'-'),'. \fR')"/>
<xsl:apply-templates/>
<xsl:text>.br&#10;</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>&#10;&#10;</xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

Просмотреть файл

@ -2,15 +2,15 @@ CONFIGURATION FILE
------------------ ------------------
The git configuration file contains a number of variables that affect The git configuration file contains a number of variables that affect
the git command's behavior. `.git/config` file for each repository the git command's behavior. The `.git/config` file in each repository
is used to store the information for that repository, and is used to store the configuration for that repository, and
`$HOME/.gitconfig` is used to store per user information to give `$HOME/.gitconfig` is used to store a per-user configuration as
fallback values for `.git/config` file. The file `/etc/gitconfig` fallback values for the `.git/config` file. The file `/etc/gitconfig`
can be used to store system-wide defaults. can be used to store a system-wide default configuration.
They can be used by both the git plumbing The configuration variables are used by both the git plumbing
and the porcelains. The variables are divided into sections, where and the porcelains. The variables are divided into sections, wherein
in the fully qualified variable name the variable itself is the last the fully qualified variable name of the variable itself is the last
dot-separated segment and the section name is everything before the last dot-separated segment and the section name is everything before the last
dot. The variable names are case-insensitive and only alphanumeric dot. The variable names are case-insensitive and only alphanumeric
characters are allowed. Some variables may appear multiple times. characters are allowed. Some variables may appear multiple times.
@ -25,35 +25,35 @@ blank lines are ignored.
The file consists of sections and variables. A section begins with The file consists of sections and variables. A section begins with
the name of the section in square brackets and continues until the next the name of the section in square brackets and continues until the next
section begins. Section names are not case sensitive. Only alphanumeric 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 must belong to some section, which means that there must be a section
header before first setting of a variable. header before the first setting of a variable.
Sections can be further divided into subsections. To begin a subsection Sections can be further divided into subsections. To begin a subsection
put its name in double quotes, separated by space from the section name, put its name in double quotes, separated by space from the section name,
in the section header, like in example below: in the section header, like in the example below:
-------- --------
[section "subsection"] [section "subsection"]
-------- --------
Subsection names can contain any characters except newline (doublequote Subsection names are case sensitive and can contain any characters except
'`"`' and backslash have to be escaped as '`\"`' and '`\\`', newline (doublequote `"` and backslash have to be escaped as `\"` and `\\`,
respectively) and are case sensitive. Section header cannot span multiple respectively). Section headers cannot span multiple
lines. Variables may belong directly to a section or to a given subsection. lines. Variables may belong directly to a section or to a given subsection.
You can have `[section]` if you have `[section "subsection"]`, but you You can have `[section]` if you have `[section "subsection"]`, but you
don't need to. don't need to.
There is also (case insensitive) alternative `[section.subsection]` syntax. There is also a case insensitive alternative `[section.subsection]` syntax.
In this syntax subsection names follow the same restrictions as for section In this syntax, subsection names follow the same restrictions as for section
name. names.
All the other lines are recognized as setting variables, in the form 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 '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". is taken as 'name' and the variable is recognized as boolean "true".
The variable names are case-insensitive and only alphanumeric 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. for a given variable; we say then that variable is multivalued.
Leading and trailing whitespace in a variable value is discarded. Leading and trailing whitespace in a variable value is discarded.
@ -61,26 +61,26 @@ Internal whitespace within a variable value is retained verbatim.
The values following the equals sign in variable assign are all either The values following the equals sign in variable assign are all either
a string, an integer, or a boolean. Boolean values may be given as yes/no, a string, an integer, or a boolean. Boolean values may be given as yes/no,
0/1 or true/false. Case is not significant in boolean values, when 0/1, true/false or on/off. Case is not significant in boolean values, when
converting value to the canonical form using '--bool' type specifier; converting value to the canonical form using '--bool' type specifier;
'git-config' will ensure that the output is "true" or "false". 'git-config' will ensure that the output is "true" or "false".
String values may be entirely or partially enclosed in double quotes. String values may be entirely or partially enclosed in double quotes.
You need to enclose variable value in double quotes if you want to You need to enclose variable values in double quotes if you want to
preserve leading or trailing whitespace, or if variable value contains preserve leading or trailing whitespace, or if the variable value contains
beginning of comment characters (if it contains '#' or ';'). comment characters (i.e. it contains '#' or ';').
Double quote '`"`' and backslash '`\`' characters in variable value must Double quote `"` and backslash `\` characters in variable values must
be escaped: use '`\"`' for '`"`' and '`\\`' for '`\`'. be escaped: use `\"` for `"` and `\\` for `\`.
The following escape sequences (beside '`\"`' and '`\\`') are recognized: The following escape sequences (beside `\"` and `\\`) are recognized:
'`\n`' for newline character (NL), '`\t`' for horizontal tabulation (HT, TAB) `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB)
and '`\b`' for backspace (BS). No other char escape sequence, nor octal and `\b` for backspace (BS). No other char escape sequence, nor octal
char sequences are valid. char sequences are valid.
Variable value ending in a '`\`' is continued on the next line in the Variable values ending in a `\` are continued on the next line in the
customary UNIX fashion. customary UNIX fashion.
Some variables may require special value format. Some variables may require a special value format.
Example Example
~~~~~~~ ~~~~~~~
@ -221,6 +221,11 @@ core.gitProxy::
Can be overridden by the 'GIT_PROXY_COMMAND' environment variable Can be overridden by the 'GIT_PROXY_COMMAND' environment variable
(which always applies universally, without the special "for" (which always applies universally, without the special "for"
handling). 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:: core.ignoreStat::
If true, commands which modify both the working tree and the index If true, commands which modify both the working tree and the index
@ -384,9 +389,9 @@ core.pager::
to override git's default settings this way, you need to override git's default settings this way, you need
to be explicit. For example, to disable the S option to be explicit. For example, to disable the S option
in a backward compatible manner, set `core.pager` 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 shell by git, which will translate the final command to
"`LESS=FRSX less -+FRSX -FRX`". `LESS=FRSX less -+FRSX -FRX`.
core.whitespace:: core.whitespace::
A comma separated list of common whitespace problems to A comma separated list of common whitespace problems to
@ -424,6 +429,15 @@ relatively high IO latencies. With this set to 'true', git will do the
index comparison to the filesystem data in parallel, allowing index comparison to the filesystem data in parallel, allowing
overlapping IO's. overlapping IO's.
core.createObject::
You can set this to 'link', in which case a hardlink followed by
a delete of the source are used to make sure that object creation
will not overwrite existing objects.
+
On some file system/operating system combinations, this is unreliable.
Set this config setting to 'rename' there; However, This will remove the
check that makes sure that existing object files will not get overwritten.
alias.*:: alias.*::
Command aliases for the linkgit:git[1] command wrapper - e.g. Command aliases for the linkgit:git[1] command wrapper - e.g.
after defining "alias.last = cat-file commit HEAD", the invocation after defining "alias.last = cat-file commit HEAD", the invocation
@ -470,10 +484,14 @@ branch.autosetuprebase::
This option defaults to never. This option defaults to never.
branch.<name>.remote:: branch.<name>.remote::
When in branch <name>, it tells 'git-fetch' which remote to fetch. When in branch <name>, it tells 'git-fetch' and 'git-push' which
If this option is not given, 'git-fetch' defaults to remote "origin". 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:: 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 and can also affect 'git-push' (see push.default).
When in branch <name>, it tells 'git-fetch' the default When in branch <name>, it tells 'git-fetch' the default
refspec to be marked for merging in FETCH_HEAD. The value is refspec to be marked for merging in FETCH_HEAD. The value is
handled like the remote part of a refspec, and must match a handled like the remote part of a refspec, and must match a
@ -550,6 +568,25 @@ color.diff.<slot>::
whitespace errors). The values of these variables may be specified as whitespace errors). The values of these variables may be specified as
in color.branch.<slot>. 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:: color.interactive::
When set to `always`, always use colors for interactive prompts When set to `always`, always use colors for interactive prompts
and displays (such as those used by "git-add --interactive"). and displays (such as those used by "git-add --interactive").
@ -567,6 +604,12 @@ color.pager::
A boolean to enable/disable colored output when the pager is in A boolean to enable/disable colored output when the pager is in
use (default is true). use (default is true).
color.showbranch::
A boolean to enable/disable color in the output of
linkgit:git-show-branch[1]. May be set to `always`,
`false` (or `never`) or `auto` (or `true`), in which case colors are used
only when the output is to a terminal. Defaults to false.
color.status:: color.status::
A boolean to enable/disable color in the output of A boolean to enable/disable color in the output of
linkgit:git-status[1]. May be set to `always`, linkgit:git-status[1]. May be set to `always`,
@ -641,6 +684,27 @@ diff.suppressBlankEmpty::
A boolean to inhibit the standard behavior of printing a space A boolean to inhibit the standard behavior of printing a space
before each empty output line. Defaults to false. before each empty output line. Defaults to false.
diff.tool::
Controls which diff tool is used. `diff.tool` overrides
`merge.tool` when used by linkgit:git-difftool[1] and has
the same valid values as `merge.tool` minus "tortoisemerge"
and plus "kompare".
difftool.<tool>.path::
Override the path for the given tool. This is useful in case
your tool is not in the PATH.
difftool.<tool>.cmd::
Specify the command to invoke the specified diff tool.
The specified command is evaluated in shell 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'
is set to the name of the temporary file containing the contents
of the diff post-image.
difftool.prompt::
Prompt before each invocation of the diff tool.
diff.wordRegex:: diff.wordRegex::
A POSIX Extended Regular Expression used to determine what is a "word" A POSIX Extended Regular Expression used to determine what is a "word"
when performing word-by-word difference calculations. Character when performing word-by-word difference calculations. Character
@ -658,6 +722,13 @@ fetch.unpackLimit::
especially on slow filesystems. If not set, the value of especially on slow filesystems. If not set, the value of
`transfer.unpackLimit` is used instead. `transfer.unpackLimit` is used instead.
format.attach::
Enable multipart/mixed attachments as the default for
'format-patch'. The value can also be a double quoted string
which will enable attachments as the default and set the
value as the boundary. See the --attach option in
linkgit:git-format-patch[1].
format.numbered:: format.numbered::
A boolean which can enable or disable sequence numbers in patch A boolean which can enable or disable sequence numbers in patch
subjects. It defaults to "auto" which enables it only if there subjects. It defaults to "auto" which enables it only if there
@ -669,6 +740,14 @@ format.headers::
Additional email headers to include in a patch to be submitted Additional email headers to include in a patch to be submitted
by mail. See linkgit:git-format-patch[1]. by mail. See linkgit:git-format-patch[1].
format.cc::
Additional "Cc:" headers to include in a patch to be submitted
by mail. See the --cc option in linkgit:git-format-patch[1].
format.subjectprefix::
The default for format-patch is to output files with the '[PATCH]'
subject prefix. Use this variable to change that prefix.
format.suffix:: format.suffix::
The default for format-patch is to output files with the suffix The default for format-patch is to output files with the suffix
`.patch`. Use this variable to change that suffix (make sure to `.patch`. Use this variable to change that suffix (make sure to
@ -679,6 +758,23 @@ format.pretty::
See linkgit:git-log[1], linkgit:git-show[1], See linkgit:git-log[1], linkgit:git-show[1],
linkgit:git-whatchanged[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.
format.signoff::
A boolean value which lets you enable the `-s/--signoff` option of
format-patch by default. *Note:* Adding the Signed-off-by: line to a
patch should be a conscious act and means that you certify you have
the rights to submit this work under the same open source license.
Please see the 'SubmittingPatches' document for further discussion.
gc.aggressiveWindow:: gc.aggressiveWindow::
The window size parameter used in the delta compression The window size parameter used in the delta compression
algorithm used by 'git-gc --aggressive'. This defaults algorithm used by 'git-gc --aggressive'. This defaults
@ -1153,7 +1249,7 @@ pager.<cmd>::
particular git subcommand when writing to a tty. If particular git subcommand when writing to a tty. If
`\--paginate` or `\--no-pager` is specified on the command line, `\--paginate` or `\--no-pager` is specified on the command line,
it takes precedence over this option. To disable pagination for 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:: pull.octopus::
The default merge strategy to use when pulling multiple branches The default merge strategy to use when pulling multiple branches
@ -1162,6 +1258,23 @@ pull.octopus::
pull.twohead:: pull.twohead::
The default merge strategy to use when pulling a single branch. 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 its upstream branch.
* `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:: receive.fsckObjects::
If it is set to true, git-receive-pack will check all received 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 objects. It will abort in the case of a malformed object or a

Просмотреть файл

@ -16,6 +16,7 @@ body blockquote {
html body { html body {
margin: 1em 5% 1em 5%; margin: 1em 5% 1em 5%;
line-height: 1.2; line-height: 1.2;
font-family: sans-serif;
} }
body div { body div {
@ -128,6 +129,15 @@ body pre {
tt.literal, code.literal { tt.literal, code.literal {
color: navy; color: navy;
font-family: sans-serif;
}
code.literal:before { content: "'"; }
code.literal:after { content: "'"; }
em {
font-style: italic;
color: #064;
} }
div.literallayout p { div.literallayout p {
@ -137,7 +147,6 @@ div.literallayout p {
div.literallayout { div.literallayout {
font-family: monospace; font-family: monospace;
# margin: 0.5em 10% 0.5em 1em;
margin: 0em; margin: 0em;
color: navy; color: navy;
border: 1px solid silver; border: 1px solid silver;
@ -187,7 +196,8 @@ dt {
} }
dt span.term { dt span.term {
font-style: italic; font-style: normal;
color: navy;
} }
div.variablelist dd p { div.variablelist dd p {

Просмотреть файл

@ -9,7 +9,7 @@ SYNOPSIS
-------- --------
[verse] [verse]
'git add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p] 'git add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p]
[--all | [--update | -u]] [--intent-to-add | -N] [--edit | -e] [--all | [--update | -u]] [--intent-to-add | -N]
[--refresh] [--ignore-errors] [--] <filepattern>... [--refresh] [--ignore-errors] [--] <filepattern>...
DESCRIPTION DESCRIPTION
@ -76,6 +76,15 @@ OPTIONS
bypassed and the 'patch' subcommand is invoked using each of bypassed and the 'patch' subcommand is invoked using each of
the specified filepatterns before exiting. the specified filepatterns before exiting.
-e, \--edit::
Open the diff vs. the index in an editor and let the user
edit it. After the editor was closed, adjust the hunk headers
and apply the patch to the index.
+
*NOTE*: Obviously, if you change anything else than the first character
on lines beginning with a space or a minus, the patch will no longer
apply.
-u:: -u::
--update:: --update::
Update only files that git already knows about, staging modified Update only files that git already knows about, staging modified

Просмотреть файл

@ -10,6 +10,7 @@ SYNOPSIS
-------- --------
[verse] [verse]
'git archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>] 'git archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]
[--output=<file>] [--worktree-attributes]
[--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish> [--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish>
[path...] [path...]
@ -47,6 +48,12 @@ OPTIONS
--prefix=<prefix>/:: --prefix=<prefix>/::
Prepend <prefix>/ to each filename in the archive. Prepend <prefix>/ to each filename in the archive.
--output=<file>::
Write the archive to <file> instead of stdout.
--worktree-attributes::
Look for attributes in .gitattributes in working directory too.
<extra>:: <extra>::
This can be any options that the archiver backend understands. This can be any options that the archiver backend understands.
See next section. See next section.

Просмотреть файл

@ -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: 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 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 $ 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: * 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, outside the repository to prevent interactions between the bisect,
make and test processes and the scripts. 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 Author
------ ------
Written by Linus Torvalds <torvalds@osdl.org> Written by Linus Torvalds <torvalds@osdl.org>

Просмотреть файл

@ -76,8 +76,8 @@ OPTIONS
based sha1 expressions such as "<branchname>@\{yesterday}". based sha1 expressions such as "<branchname>@\{yesterday}".
-f:: -f::
Force the creation of a new branch even if it means deleting Reset <branchname> to <startpoint> if <branchname> exists
a branch that already exists with the same name. already. Without `-f` 'git-branch' refuses to change an existing branch.
-m:: -m::
Move/rename a branch and the corresponding reflog. Move/rename a branch and the corresponding reflog.
@ -100,7 +100,9 @@ OPTIONS
-v:: -v::
--verbose:: --verbose::
Show sha1 and commit subject line for each head. Show sha1 and commit subject line for each head, along with
relationship to upstream branch (if any). If given twice, print
the name of the upstream branch, as well.
--abbrev=<length>:: --abbrev=<length>::
Alter the sha1's minimum display length in the output listing. Alter the sha1's minimum display length in the output listing.
@ -109,20 +111,24 @@ OPTIONS
--no-abbrev:: --no-abbrev::
Display the full sha1s in the output listing rather than abbreviating them. Display the full sha1s in the output listing rather than abbreviating them.
-t::
--track:: --track::
When creating a new branch, set up the configuration so that 'git-pull' When creating a new branch, set up configuration to mark the
will automatically retrieve data from the start point, which must be start-point branch as "upstream" from the new branch. This
a branch. Use this if you always pull from the same upstream branch configuration will tell git to show the relationship between the
into the new branch, and if you do not want to use "git pull two branches in `git status` and `git branch -v`. Furthermore,
<repository> <refspec>" explicitly. This behavior is the default it directs `git pull` without arguments to pull from the
when the start point is a remote branch. Set the upstream when the new branch is checked out.
branch.autosetupmerge configuration variable to `false` if you want +
'git-checkout' and 'git-branch' to always behave as if '--no-track' were This behavior is the default when the start point is a remote branch.
given. Set it to `always` if you want this behavior when the Set the branch.autosetupmerge configuration variable to `false` if you
start-point is either a local or remote branch. want `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.
--no-track:: --no-track::
Ignore the branch.autosetupmerge configuration variable. Do not set up "upstream" configuration, even if the
branch.autosetupmerge configuration variable is true.
--contains <commit>:: --contains <commit>::
Only list branches which contain the specified commit. Only list branches which contain the specified commit.

Просмотреть файл

@ -7,7 +7,9 @@ git-check-ref-format - Ensures that a reference name is well formed
SYNOPSIS SYNOPSIS
-------- --------
[verse]
'git check-ref-format' <refname> 'git check-ref-format' <refname>
'git check-ref-format' [--branch] <branchname-shorthand>
DESCRIPTION DESCRIPTION
----------- -----------
@ -23,6 +25,10 @@ imposes the following rules on how references are named:
grouping, but no slash-separated component can begin with a grouping, but no slash-separated component can begin with a
dot `.`. dot `.`.
. They must contain at least one `/`. This enforces the presence of a
category like `heads/`, `tags/` etc. but the actual names are not
restricted.
. They cannot have two consecutive dots `..` anywhere. . They cannot have two consecutive dots `..` anywhere.
. They cannot have ASCII control characters (i.e. bytes whose . They cannot have ASCII control characters (i.e. bytes whose
@ -30,7 +36,13 @@ imposes the following rules on how references are named:
caret `{caret}`, colon `:`, question-mark `?`, asterisk `*`, caret `{caret}`, colon `:`, question-mark `?`, asterisk `*`,
or open bracket `[` anywhere. or open bracket `[` anywhere.
. They cannot end with a slash `/`. . They cannot end with a slash `/` nor a dot `.`.
. They cannot end with the sequence `.lock`.
. They cannot contain a sequence `@{`.
- They cannot contain a `\\`.
These rules make it easy for shell script based tools to parse These rules make it easy for shell script based tools to parse
reference names, pathname expansion by the shell when a reference name is used reference names, pathname expansion by the shell when a reference name is used
@ -49,6 +61,18 @@ reference name expressions (see linkgit:git-rev-parse[1]):
It may also be used to select a specific object such as with It may also be used to select a specific object such as with
'git-cat-file': "git cat-file blob v1.3.3:refs.c". 'git-cat-file': "git cat-file blob v1.3.3:refs.c".
. at-open-brace `@{` is used as a notation to access a reflog entry.
With the `--branch` option, it expands a branch name shorthand and
prints the name of the branch the shorthand refers to.
EXAMPLE
-------
git check-ref-format --branch @{-1}::
Print the name of the previous branch.
GIT GIT
--- ---

Просмотреть файл

@ -8,28 +8,28 @@ git-checkout - Checkout a branch or paths to the working tree
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git checkout' [-q] [-f] [--track | --no-track] [-b <new_branch> [-l]] [-m] [<branch>] 'git checkout' [-q] [-f] [-m] [<branch>]
'git checkout' [-q] [-f] [-m] [-b <new_branch>] [<start_point>]
'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>... 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
DESCRIPTION DESCRIPTION
----------- -----------
When <paths> are not given, this command switches branches by When <paths> are not given, this command switches branches by
updating the index and working tree to reflect the specified updating the index, working tree, and HEAD to reflect the specified
branch, <branch>, and updating HEAD to be <branch> or, if branch.
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 If `-b` is given, a new branch is created and checked out, as if
name is constructed from the specified branch name by stripping linkgit:git-branch[1] were called; in this case you can
the first namespace level. use the --track or --no-track options, which will be passed to `git
branch`. As a convenience, --track without `-b` implies branch
creation; see the description of --track below.
When <paths> are given, this command does *not* switch When <paths> are given, this command does *not* switch
branches. It updates the named paths in the working tree from 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 the index file, or from a named <tree-ish> (most often a commit). In
this case, the `-b` options is meaningless and giving this case, the `-b` and `--track` options are meaningless and giving
either of them results in an error. <tree-ish> argument can be 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) used to specify a specific tree-ish (i.e. commit, tag or tree)
to update the index for the given paths before updating the to update the index for the given paths before updating the
working tree. working tree.
@ -62,27 +62,16 @@ entries; instead, unmerged entries are ignored.
-b:: -b::
Create a new branch named <new_branch> and start it at Create a new branch named <new_branch> and start it at
<branch>. The new branch name must pass all checks defined <start_point>; see linkgit:git-branch[1] for details.
by linkgit:git-check-ref-format[1]. Some of these checks
may restrict the characters allowed in a branch name.
-t:: -t::
--track:: --track::
When creating a new branch, set up configuration so that 'git-pull' When creating a new branch, set up "upstream" configuration. See
will automatically retrieve data from the start point, which must be "--track" in linkgit:git-branch[1] for details.
a branch. Use this if you always pull from the same upstream branch
into the new branch, and if you don't want to use "git pull
<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
given. Set it to `always` if you want this behavior when the
start-point is either a local or remote branch.
+ +
If no '-b' option was given, the name of the new branch will be If no '-b' option is given, the name of the new branch will be
derived from the remote branch, by attempting to guess the name derived from the remote branch. If "remotes/" or "refs/remotes/"
of the branch on remote system. If "remotes/" or "refs/remotes/" is prefixed it is stripped away, and then the part up to the
are prefixed, it is stripped away, and then the part up to the
next slash (which would be the nickname of the remote) is removed. 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 This would tell us to use "hack" as the local branch when branching
off of "origin/hack" (or "remotes/origin/hack", or even off of "origin/hack" (or "remotes/origin/hack", or even
@ -91,12 +80,12 @@ guessing results in an empty name, the guessing is aborted. You can
explicitly give a name with '-b' in such a case. explicitly give a name with '-b' in such a case.
--no-track:: --no-track::
Ignore the branch.autosetupmerge configuration variable. Do not set up "upstream" configuration, even if the
branch.autosetupmerge configuration variable is true.
-l:: -l::
Create the new branch's reflog. This activates recording of Create the new branch's reflog; see linkgit:git-branch[1] for
all changes made to the branch ref, enabling use of date details.
based sha1 expressions such as "<branchname>@\{yesterday}".
-m:: -m::
--merge:: --merge::
@ -124,23 +113,28 @@ the conflicted merge in the specified paths.
"merge" (default) and "diff3" (in addition to what is shown by "merge" (default) and "diff3" (in addition to what is shown by
"merge" style, shows the original contents). "merge" style, shows the original contents).
<branch>::
Branch to checkout; if it refers to a branch (i.e., a name that,
when prepended with "refs/heads/", is a valid ref), then that
branch is checked out. Otherwise, if it refers to a valid
commit, your HEAD becomes "detached" and you are no longer on
any branch (see below for details).
+
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\}"`.
<new_branch>:: <new_branch>::
Name for the new branch. Name for the new branch.
<start_point>::
The name of a commit at which to start the new branch; see
linkgit:git-branch[1] for details. Defaults to HEAD.
<tree-ish>:: <tree-ish>::
Tree to checkout from (when paths are given). If not specified, Tree to checkout from (when paths are given). If not specified,
the index will be used. the index will be used.
<branch>::
Branch to checkout (when no paths are given); may be any object
ID that resolves to a commit. Defaults to HEAD.
+
When this parameter names a non-branch (but still a valid commit object),
your HEAD becomes 'detached'.
+
As a special case, the "`@\{-N\}`" syntax for the N-th last branch
checks out the branch (instead of detaching). You may also specify
"`-`" which is synonymous with "`@\{-1\}`".
Detached HEAD Detached HEAD
@ -156,12 +150,12 @@ $ git checkout v2.6.18
------------ ------------
Earlier versions of git did not allow this and asked you to 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 version 1.5.0, the above command 'detaches' your HEAD from the
current branch and directly point at the commit named by the tag current branch and directly points at the commit named by the tag
(`v2.6.18` in the above example). (`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 `git reset --hard $othercommit` to further move around, for
example. You can make changes and create a new commit on top of 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 a detached HEAD. You can even create a merge by using `git
@ -206,7 +200,7 @@ You should instead write:
$ git checkout -- hello.c $ 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: branch would be done using:
+ +
------------ ------------
@ -214,7 +208,7 @@ $ git checkout mytopic
------------ ------------
+ +
However, your "wrong" branch and correct "mytopic" branch may 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: the above checkout would fail like this:
+ +
------------ ------------

Просмотреть файл

@ -12,14 +12,17 @@ SYNOPSIS
DESCRIPTION DESCRIPTION
----------- -----------
Removes files unknown to git. This allows to clean the working tree
from files that are not under version control. If the '-x' option is Cleans the working tree by recursively removing files that are not
specified, ignored files are also removed, allowing to remove all under version control, starting from the current directory.
build products.
Normally, only files unknown to git are removed, but if the '-x'
option is specified, ignored files are also removed. This can, for
example, be useful to remove all build products.
If any optional `<path>...` arguments are given, only those paths If any optional `<path>...` arguments are given, only those paths
are affected. are affected.
OPTIONS OPTIONS
------- -------
-d:: -d::

Просмотреть файл

@ -149,7 +149,7 @@ then the cloned repository will become corrupt.
part of the source repository is used if no directory is part of the source repository is used if no directory is
explicitly given ("repo" for "/path/to/repo.git" and "foo" explicitly given ("repo" for "/path/to/repo.git" and "foo"
for "host.xz:foo/.git"). Cloning into an existing directory for "host.xz:foo/.git"). Cloning into an existing directory
is not allowed. is only allowed if the directory is empty.
:git-clone: 1 :git-clone: 1
include::urls.txt[] include::urls.txt[]

Просмотреть файл

@ -11,7 +11,7 @@ SYNOPSIS
[verse] [verse]
'git config' [<file-option>] [type] [-z|--null] name [value [value_regex]] 'git config' [<file-option>] [type] [-z|--null] name [value [value_regex]]
'git config' [<file-option>] [type] --add name value '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 name [value_regex]
'git config' [<file-option>] [type] [-z|--null] --get-all 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] '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>] [-z|--null] -l | --list
'git config' [<file-option>] --get-color name [default] 'git config' [<file-option>] --get-color name [default]
'git config' [<file-option>] --get-colorbool name [stdout-is-tty] 'git config' [<file-option>] --get-colorbool name [stdout-is-tty]
'git config' [<file-option>] -e | --edit
DESCRIPTION DESCRIPTION
----------- -----------
@ -68,7 +69,8 @@ OPTIONS
--add:: --add::
Adds a new line to the option without altering any existing Adds a new line to the option without altering any existing
values. This is the same as providing '^$' as the value_regex. values. This is the same as providing '^$' as the value_regex
in `--replace-all`.
--get:: --get::
Get the value for a given key (optionally filtered by a regex Get the value for a given key (optionally filtered by a regex
@ -154,13 +156,18 @@ See also <<FILES>>.
When the color setting for `name` is undefined, the command uses When the color setting for `name` is undefined, the command uses
`color.ui` as fallback. `color.ui` as fallback.
--get-color name default:: --get-color name [default]::
Find the color configured for `name` (e.g. `color.diff.new`) and Find the color configured for `name` (e.g. `color.diff.new`) and
output it as the ANSI color escape sequence to the standard output it as the ANSI color escape sequence to the standard
output. The optional `default` parameter is used instead, if output. The optional `default` parameter is used instead, if
there is no color configured for `name`. 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]]
FILES FILES
----- -----

Просмотреть файл

@ -24,6 +24,9 @@ repository, or incrementally import into an existing one.
Splitting the CVS log into patch sets is done by 'cvsps'. Splitting the CVS log into patch sets is done by 'cvsps'.
At least version 2.1 is required. 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 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 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 "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 Otherwise, success is indicated the Unix way, i.e. by simply exiting with
a zero exit status. 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 practice:
* cvs2git (part of cvs2svn), `http://cvs2svn.tigris.org`
* parsecvs, `http://cgit.freedesktop.org/~keithp/parsecvs`
Author Author
------ ------

Просмотреть файл

@ -3,52 +3,54 @@ git-difftool(1)
NAME NAME
---- ----
git-difftool - compare changes using common merge tools git-difftool - Show changes using common diff tools
SYNOPSIS SYNOPSIS
-------- --------
'git difftool' [--tool=<tool>] [--no-prompt] ['git diff' options] 'git difftool' [--tool=<tool>] [-y|--no-prompt|--prompt] [<'git diff' options>]
DESCRIPTION DESCRIPTION
----------- -----------
'git-difftool' is a git command that allows you to compare and edit files 'git-difftool' is a git command that allows you to compare and edit files
between revisions using common merge tools. At its most basic level, between revisions using common diff tools. 'git difftool' is a frontend
'git-difftool' does what 'git-mergetool' does but its use is for non-merge to 'git-diff' and accepts the same options and arguments.
situations such as when preparing commits or comparing changes against
the index.
'git difftool' is a frontend to 'git diff' and accepts the same
arguments and options.
See linkgit:git-diff[1] for the full list of supported options.
OPTIONS OPTIONS
------- -------
-y::
--no-prompt::
Do not prompt before launching a diff tool.
--prompt::
Prompt before each invocation of the diff tool.
This is the default behaviour; the option is provided to
override any configuration settings.
-t <tool>:: -t <tool>::
--tool=<tool>:: --tool=<tool>::
Use the merge resolution program specified by <tool>. Use the diff tool specified by <tool>.
Valid merge tools are: Valid merge tools are:
kdiff3, kompare, tkdiff, meld, xxdiff, emerge, kdiff3, kompare, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff,
vimdiff, gvimdiff, ecmerge, and opendiff ecmerge, diffuse and opendiff
+ +
If a merge resolution program is not specified, 'git-difftool' If a diff tool is not specified, 'git-difftool'
will use the configuration variable `merge.tool`. If the will use the configuration variable `diff.tool`. If the
configuration variable `merge.tool` is not set, 'git difftool' configuration variable `diff.tool` is not set, 'git-difftool'
will pick a suitable default. will pick a suitable default.
+ +
You can explicitly provide a full path to the tool by setting the 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 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. tool is available in PATH.
+ +
Instead of running one of the known merge tool programs, Instead of running one of the known diff tools,
'git-difftool' can be customized to run an alternative program 'git-difftool' can be customized to run an alternative program
by specifying the command line to invoke in a configuration 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 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 the configured command line will be invoked with the following
variables available: `$LOCAL` is set to the name of the temporary variables available: `$LOCAL` is set to the name of the temporary
file containing the contents of the diff pre-image and `$REMOTE` file containing the contents of the diff pre-image and `$REMOTE`
@ -56,29 +58,27 @@ is set to the name of the temporary file containing the contents
of the diff post-image. `$BASE` is provided for compatibility of the diff post-image. `$BASE` is provided for compatibility
with custom merge tool commands and has the same value as `$LOCAL`. with custom merge tool commands and has the same value as `$LOCAL`.
--no-prompt:: See linkgit:git-diff[1] for the full list of supported options.
Do not prompt before launching a diff tool.
CONFIG VARIABLES CONFIG VARIABLES
---------------- ----------------
merge.tool:: 'git-difftool' falls back to 'git-mergetool' config variables when the
The default merge tool to use. difftool equivalents have not been defined.
+
See the `--tool=<tool>` option above for more details.
merge.keepBackup:: diff.tool::
The original, unedited file content can be saved to a file with The default diff tool to use.
a `.orig` extension. Defaults to `true` (i.e. keep the backup files).
mergetool.<tool>.path:: difftool.<tool>.path::
Override the path for the given tool. This is useful in case Override the path for the given tool. This is useful in case
your tool is not in the PATH. your tool is not in the PATH.
mergetool.<tool>.cmd:: difftool.<tool>.cmd::
Specify the command to invoke the specified merge tool. Specify the command to invoke the specified diff tool.
+ +
See the `--tool=<tool>` option above for more details. See the `--tool=<tool>` option above for more details.
difftool.prompt::
Prompt before each invocation of the diff tool.
SEE ALSO SEE ALSO
-------- --------

Просмотреть файл

@ -94,7 +94,9 @@ OPTIONS
--index-filter <command>:: --index-filter <command>::
This is the filter for rewriting the index. It is similar to the 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 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>:: --parent-filter <command>::
This is the filter for rewriting the commit's parent list. This is the filter for rewriting the commit's parent list.
@ -207,19 +209,18 @@ However, if the file is absent from the tree of some commit,
a simple `rm filename` will fail for that tree and 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. 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. 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 To rewrite the repository to look as if `foodir/` had been its project
root, and discard all other history: root, and discard all other history:

Просмотреть файл

@ -75,6 +75,8 @@ For all objects, the following names can be used:
refname:: refname::
The name of the ref (the part after $GIT_DIR/). The name of the ref (the part after $GIT_DIR/).
For a non-ambiguous short name of the ref append `:short`. For a non-ambiguous short name of the ref append `:short`.
The option core.warnAmbiguousRefs is used to select the strict
abbreviation mode.
objecttype:: objecttype::
The type of the object (`blob`, `tree`, `commit`, `tag`). The type of the object (`blob`, `tree`, `commit`, `tag`).
@ -85,6 +87,11 @@ objectsize::
objectname:: objectname::
The object name (aka SHA-1). The object name (aka SHA-1).
upstream::
The name of a local ref which can be considered ``upstream''
from the displayed ref. Respects `:short` in the same way as
`refname` above.
In addition to the above, for commit and tag objects, the header In addition to the above, for commit and tag objects, the header
field names (`tree`, `parent`, `object`, `type`, and `tag`) can field names (`tree`, `parent`, `object`, `type`, and `tag`) can
be used to specify the value in the header field. be used to specify the value in the header field.

Просмотреть файл

@ -9,9 +9,10 @@ git-format-patch - Prepare patches for e-mail submission
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git format-patch' [-k] [-o <dir> | --stdout] [--thread] 'git format-patch' [-k] [(-o|--output-directory) <dir> | --stdout]
[--attach[=<boundary>] | --inline[=<boundary>]] [--thread[=<style>]]
[-s | --signoff] [<common diff options>] [(--attach|--inline)[=<boundary>] | --no-attach]
[-s | --signoff]
[-n | --numbered | -N | --no-numbered] [-n | --numbered | -N | --no-numbered]
[--start-number <n>] [--numbered-files] [--start-number <n>] [--numbered-files]
[--in-reply-to=Message-Id] [--suffix=.<sfx>] [--in-reply-to=Message-Id] [--suffix=.<sfx>]
@ -19,6 +20,7 @@ SYNOPSIS
[--subject-prefix=Subject-Prefix] [--subject-prefix=Subject-Prefix]
[--cc=<email>] [--cc=<email>]
[--cover-letter] [--cover-letter]
[<common diff options>]
[ <since> | <revision range> ] [ <since> | <revision range> ]
DESCRIPTION DESCRIPTION
@ -112,15 +114,27 @@ include::diff-options.txt[]
which is the commit message and the patch itself in the which is the commit message and the patch itself in the
second part, with "Content-Disposition: attachment". second part, with "Content-Disposition: attachment".
--no-attach::
Disable the creation of an attachment, overriding the
configuration setting.
--inline[=<boundary>]:: --inline[=<boundary>]::
Create multipart/mixed attachment, the first part of Create multipart/mixed attachment, the first part of
which is the commit message and the patch itself in the which is the commit message and the patch itself in the
second part, with "Content-Disposition: inline". second part, with "Content-Disposition: inline".
--thread:: --thread[=<style>]::
Add In-Reply-To and References headers to make the second and Add In-Reply-To and References headers to make the second and
subsequent mails appear as replies to the first. Also generates subsequent mails appear as replies to the first. Also generates
the Message-Id header to reference. 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:: --in-reply-to=Message-Id::
Make the first mail (or all the mails with --no-thread) appear as a Make the first mail (or all the mails with --no-thread) appear as a
@ -144,6 +158,11 @@ include::diff-options.txt[]
Add a "Cc:" header to the email headers. This is in addition Add a "Cc:" header to the email headers. This is in addition
to any configured headers, and may be used multiple times. 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:: --cover-letter::
In addition to the patches, generate a cover letter file In addition to the patches, generate a cover letter file
containing the shortlog and the overall diffstat. You can containing the shortlog and the overall diffstat. You can
@ -152,18 +171,17 @@ include::diff-options.txt[]
--suffix=.<sfx>:: --suffix=.<sfx>::
Instead of using `.patch` as the suffix for generated Instead of using `.patch` as the suffix for generated
filenames, use specified suffix. A common alternative is filenames, use specified suffix. A common alternative is
`--suffix=.txt`. `--suffix=.txt`. Leaving this empty will remove the `.patch`
suffix.
+ +
Note that you would need to include the leading dot `.` if you Note that the leading character does not have to be a dot; for example,
want a filename like `0001-description-of-my-change.patch`, and you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.
the first letter does not have to be a dot. Leaving it empty would
not add any suffix.
--no-binary:: --no-binary::
Don't output contents of changes in binary files, just take note Do not output contents of changes in binary files, instead
that they differ. Note that this disable the patch to be properly display a notice that those files changed. Patches generated
applied. By default the contents of changes in those files are using this option cannot be applied properly, but they are
encoded in the patch. still useful for code review.
--root:: --root::
Treat the revision argument as a <revision range>, even if it Treat the revision argument as a <revision range>, even if it
@ -174,9 +192,10 @@ not add any suffix.
CONFIGURATION CONFIGURATION
------------- -------------
You can specify extra mail header lines to be added to each message You can specify extra mail header lines to be added to each message,
in the repository configuration, new defaults for the subject prefix defaults for the subject prefix and file suffix, number patches when
and file suffix, and number patches when outputting more than one. outputting more than one patch, add "Cc:" headers, configure attachments,
and sign off patches with configuration variables.
------------ ------------
[format] [format]
@ -185,6 +204,8 @@ and file suffix, and number patches when outputting more than one.
suffix = .txt suffix = .txt
numbered = auto numbered = auto
cc = <email> cc = <email>
attach [ = mime-boundary-string ]
signoff = true
------------ ------------
@ -222,8 +243,8 @@ $ git format-patch -M -B origin
+ +
Additionally, it detects and handles renames and complete rewrites Additionally, it detects and handles renames and complete rewrites
intelligently to produce a renaming patch. A renaming patch reduces intelligently to produce a renaming patch. A renaming patch reduces
the amount of text output, and generally makes it easier to review it. the amount of text output, and generally makes it easier to review.
Note that the "patch" program does not understand renaming patches, so Note that non-git "patch" programs won't understand renaming patches, so
use it only when you know the recipient uses git to apply your patch. use it only when you know the recipient uses git to apply your patch.
* Extract three topmost commits from the current branch and format them * Extract three topmost commits from the current branch and format them

Просмотреть файл

@ -17,6 +17,7 @@ SYNOPSIS
[-l | --files-with-matches] [-L | --files-without-match] [-l | --files-with-matches] [-L | --files-without-match]
[-z | --null] [-z | --null]
[-c | --count] [--all-match] [-c | --count] [--all-match]
[--color | --no-color]
[-A <post-context>] [-B <pre-context>] [-C <context>] [-A <post-context>] [-B <pre-context>] [-C <context>]
[-f <file>] [-e] <pattern> [-f <file>] [-e] <pattern>
[--and|--or|--not|(|)|-e <pattern>...] [<tree>...] [--and|--or|--not|(|)|-e <pattern>...] [<tree>...]
@ -105,6 +106,13 @@ OPTIONS
Instead of showing every matched line, show the number of Instead of showing every matched line, show the number of
lines that match. 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>:: -[ABC] <context>::
Show `context` trailing (`A` -- after), or leading (`B` Show `context` trailing (`A` -- after), or leading (`B`
-- before), or both (`C` -- context) lines, and place a -- before), or both (`C` -- context) lines, and place a

Просмотреть файл

@ -64,6 +64,13 @@ imap.sslverify::
used by the SSL/TLS connection. Default is `true`. Ignored when used by the SSL/TLS connection. Default is `true`. Ignored when
imap.tunnel is set. 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 Examples
~~~~~~~~ ~~~~~~~~

Просмотреть файл

@ -40,8 +40,8 @@ include::merge-options.txt[]
include::merge-strategies.txt[] include::merge-strategies.txt[]
If you tried a merge which resulted in a complex conflicts and If you tried a merge which resulted in complex conflicts and
would want to start over, you can recover with 'git-reset'. want to start over, you can recover with 'git-reset'.
CONFIGURATION 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 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. 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 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. And here is another line that is cleanly resolved or unmodified.
------------ ------------
In addition to the "`<<<<<<<`", "`=======`", and "`>>>>>>>`" markers, it uses In addition to the `<<<<<<<`, `=======`, and `>>>>>>>` markers, it uses
another "`|||||||`" marker that is followed by the original text. You can 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 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 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 positive attitude. You can sometimes come up with a better resolution by

Просмотреть файл

@ -0,0 +1,54 @@
git-mergetool--lib(1)
=====================
NAME
----
git-mergetool--lib - Common git merge tool shell scriptlets
SYNOPSIS
--------
'TOOL_MODE=(diff|merge) . "$(git --exec-path)/git-mergetool--lib"'
DESCRIPTION
-----------
This is not a command the end user would want to run. Ever.
This documentation is meant for people who are studying the
Porcelain-ish scripts and/or are writing new ones.
The 'git-mergetool--lib' scriptlet is designed to be sourced (using
`.`) by other shell scripts to set up functions for working
with git merge tools.
Before sourcing 'git-mergetool--lib', your script must set `TOOL_MODE`
to define the operation mode for the functions listed below.
'diff' and 'merge' are valid values.
FUNCTIONS
---------
get_merge_tool::
returns a merge tool.
get_merge_tool_cmd::
returns the custom command for a merge tool.
get_merge_tool_path::
returns the custom path for a merge tool.
run_merge_tool::
launches a merge tool given the tool name and a true/false
flag to indicate whether a merge base is present.
'$MERGED', '$LOCAL', '$REMOTE', and '$BASE' must be defined
for use by the merge tool.
Author
------
Written by David Aguilar <davvid@gmail.com>
Documentation
--------------
Documentation by David Aguilar and the git-list <git@vger.kernel.org>.
GIT
---
Part of the linkgit:git[1] suite

Просмотреть файл

@ -26,7 +26,8 @@ OPTIONS
--tool=<tool>:: --tool=<tool>::
Use the merge resolution program specified by <tool>. Use the merge resolution program specified by <tool>.
Valid merge tools are: Valid merge tools are:
kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge,
diffuse, tortoisemerge and opendiff
+ +
If a merge resolution program is not specified, 'git-mergetool' If a merge resolution program is not specified, 'git-mergetool'
will use the configuration variable `merge.tool`. If the will use the configuration variable `merge.tool`. If the

Просмотреть файл

@ -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 When dealing with 'git-diff-tree' output, it takes advantage of
the fact that the patch is prefixed with the object name of the 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. 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. This can be used to make a mapping from patch ID to commit ID.

Просмотреть файл

@ -24,8 +24,8 @@ every time you push into it, by setting up 'hooks' there. See
documentation for linkgit:git-receive-pack[1]. documentation for linkgit:git-receive-pack[1].
OPTIONS OPTIONS[[OPTIONS]]
------- ------------------
<repository>:: <repository>::
The "remote" repository that is destination of a push The "remote" repository that is destination of a push
operation. This parameter can be either a URL operation. This parameter can be either a URL
@ -187,6 +187,28 @@ reason::
Examples 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:: git push origin master::
Find a ref that matches `master` in the source repository Find a ref that matches `master` in the source repository
(most likely, it would find `refs/heads/master`), and update (most likely, it would find `refs/heads/master`), and update

Просмотреть файл

@ -192,6 +192,13 @@ Alternatively, you can undo the 'git-rebase' with
git rebase --abort git rebase --abort
CONFIGURATION
-------------
rebase.stat::
Whether to show a diffstat of what changed upstream since the last
rebase. False by default.
OPTIONS OPTIONS
------- -------
<newbase>:: <newbase>::
@ -224,15 +231,22 @@ OPTIONS
-s <strategy>:: -s <strategy>::
--strategy=<strategy>:: --strategy=<strategy>::
Use the given merge strategy; can be supplied more than Use the given merge strategy.
once to specify them in the order they should be tried.
If there is no `-s` option, a built-in list of strategies If there is no `-s` option, a built-in list of strategies
is used instead ('git-merge-recursive' when merging a single is used instead ('git-merge-recursive' when merging a single
head, 'git-merge-octopus' otherwise). This implies --merge. head, 'git-merge-octopus' otherwise). This implies --merge.
-v:: -v::
--verbose:: --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:: --no-verify::
This option bypasses the pre-rebase hook. See also linkgit:githooks[5]. This option bypasses the pre-rebase hook. See also linkgit:githooks[5].
@ -243,11 +257,23 @@ OPTIONS
context exist they all must match. By default no context is context exist they all must match. By default no context is
ever ignored. 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>:: --whitespace=<option>::
This flag is passed to the 'git-apply' program This flag is passed to the 'git-apply' program
(see linkgit:git-apply[1]) that applies the patch. (see linkgit:git-apply[1]) that applies the patch.
Incompatible with the --interactive option. 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:: -i::
--interactive:: --interactive::
Make a list of the commits which are about to be rebased. Let the Make a list of the commits which are about to be rebased. Let the

Просмотреть файл

@ -13,9 +13,10 @@ SYNOPSIS
'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url> 'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>
'git remote rename' <old> <new> 'git remote rename' <old> <new>
'git remote rm' <name> 'git remote rm' <name>
'git remote set-head' <name> [-a | -d | <branch>]
'git remote show' [-n] <name> 'git remote show' [-n] <name>
'git remote prune' [-n | --dry-run] <name> 'git remote prune' [-n | --dry-run] <name>
'git remote update' [group] 'git remote update' [-p | --prune] [group | remote]...
DESCRIPTION DESCRIPTION
----------- -----------
@ -53,8 +54,7 @@ is created. You can give more than one `-t <branch>` to track
multiple branches without grabbing all branches. multiple branches without grabbing all branches.
+ +
With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set
up to point at remote's `<master>` branch instead of whatever up to point at remote's `<master>` branch. See also the set-head command.
branch the `HEAD` at the remote repository actually points at.
+ +
In mirror mode, enabled with `\--mirror`, the refs will not be stored In mirror mode, enabled with `\--mirror`, the refs will not be stored
in the 'refs/remotes/' namespace, but in 'refs/heads/'. This option 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 Remove the remote named <name>. All remote tracking branches and
configuration settings for the remote are removed. 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':: 'show'::
Gives some information about the remote <name>. Gives some information about the remote <name>.
@ -101,6 +125,8 @@ the configuration parameter remotes.default will get used; if
remotes.default is not defined, all remotes which do not have the remotes.default is not defined, all remotes which do not have the
configuration parameter remote.<name>.skipDefaultUpdate set to true will configuration parameter remote.<name>.skipDefaultUpdate set to true will
be updated. (See linkgit:git-config[1]). be updated. (See linkgit:git-config[1]).
+
With `--prune` option, prune all the remotes that are updated.
DISCUSSION DISCUSSION

Просмотреть файл

@ -26,10 +26,15 @@ OPTIONS
--parseopt:: --parseopt::
Use 'git-rev-parse' in option parsing mode (see PARSEOPT section below). Use 'git-rev-parse' in option parsing mode (see PARSEOPT section below).
--keep-dash-dash:: --keep-dashdash::
Only meaningful in `--parseopt` mode. Tells the option parser to echo Only meaningful in `--parseopt` mode. Tells the option parser to echo
out the first `--` met instead of skipping it. out the first `--` met instead of skipping it.
--sq-quote::
Use 'git-rev-parse' in shell quoting mode (see SQ-QUOTE
section below). In contrast to the `--sq` option below, this
mode does only quoting. Nothing else is done to command input.
--revs-only:: --revs-only::
Do not output flags and parameters not meant for Do not output flags and parameters not meant for
'git-rev-list' command. 'git-rev-list' command.
@ -64,7 +69,8 @@ OPTIONS
properly quoted for consumption by shell. Useful when properly quoted for consumption by shell. Useful when
you expect your parameter to contain whitespaces and you expect your parameter to contain whitespaces and
newlines (e.g. when using pickaxe `-S` with newlines (e.g. when using pickaxe `-S` with
'git-diff-\*'). 'git-diff-\*'). In contrast to the `--sq-quote` option,
the command input is still interpreted as usual.
--not:: --not::
When showing object names, prefix them with '{caret}' and When showing object names, prefix them with '{caret}' and
@ -84,6 +90,11 @@ OPTIONS
unfortunately named tag "master"), and show them as full unfortunately named tag "master"), and show them as full
refnames (e.g. "refs/heads/master"). refnames (e.g. "refs/heads/master").
--abbrev-ref[={strict|loose}]::
A non-ambiguous short name of the objects name.
The option core.warnAmbiguousRefs is used to select the strict
abbreviation mode.
--all:: --all::
Show all refs found in `$GIT_DIR/refs`. Show all refs found in `$GIT_DIR/refs`.
@ -299,18 +310,18 @@ previous section means the set of commits reachable from that
commit, following the commit ancestry chain. commit, following the commit ancestry chain.
To exclude commits reachable from a commit, a prefix `{caret}` 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`. from `r2` but exclude the ones reachable from `r1`.
This set operation appears so often that there is a shorthand This set operation appears so often that there is a shorthand
for it. When you have two commits `r1` and `r2` (named according for it. When you have two commits `r1` and `r2` (named according
to the syntax explained in SPECIFYING REVISIONS above), you can ask to the syntax explained in SPECIFYING REVISIONS above), you can ask
for commits that are reachable from r2 excluding those that are reachable 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 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 It is the set of commits that are reachable from either one of
`r1` or `r2` but not from both. `r1` or `r2` but not from both.
@ -401,6 +412,33 @@ C? option C with an optional argument"
eval `echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?` eval `echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?`
------------ ------------
SQ-QUOTE
--------
In `--sq-quote` mode, 'git-rev-parse' echoes on the standard output a
single line suitable for `sh(1)` `eval`. This line is made by
normalizing the arguments following `--sq-quote`. Nothing other than
quoting the arguments is done.
If you want command input to still be interpreted as usual by
'git-rev-parse' before the output is shell quoted, see the `--sq`
option.
Example
~~~~~~~
------------
$ cat >your-git-script.sh <<\EOF
#!/bin/sh
args=$(git rev-parse --sq-quote "$@") # quote user-supplied arguments
command="git frotz -n24 $args" # and use it inside a handcrafted
# command line
eval "$command"
EOF
$ sh your-git-script.sh "a b'c"
------------
EXAMPLES EXAMPLES
-------- --------

Просмотреть файл

@ -39,13 +39,13 @@ OPTIONS
Composing Composing
~~~~~~~~~ ~~~~~~~~~
--bcc:: --bcc=<address>::
Specify a "Bcc:" value for each email. Default is the value of Specify a "Bcc:" value for each email. Default is the value of
'sendemail.bcc'. 'sendemail.bcc'.
+ +
The --bcc option must be repeated for each user you want on the bcc list. The --bcc option must be repeated for each user you want on the bcc list.
--cc:: --cc=<address>::
Specify a starting "Cc:" value for each email. Specify a starting "Cc:" value for each email.
Default is the value of 'sendemail.cc'. Default is the value of 'sendemail.cc'.
+ +
@ -60,33 +60,35 @@ 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 Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
introductory message for the patch series. introductory message for the patch series.
+ +
When '--compose' is used, git send-email gets less interactive will use the When '--compose' is used, git send-email will use the From, Subject, and
values of the headers you set there. If the body of the email (what you type In-Reply-To headers specified in the message. If the body of the message
after the headers and a blank line) only contains blank (or GIT: prefixed) (what you type after the headers and a blank line) only contains blank
lines, the summary won't be sent, but git-send-email will still use the (or GIT: prefixed) lines the summary won't be sent, but From, Subject,
Headers values if you don't removed them. 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 Missing From or In-Reply-To headers will be prompted for.
interactively after quitting your editor.
--from:: --from=<address>::
Specify the sender of the emails. This will default to Specify the sender of the emails. If not specified on the command line,
the value GIT_COMMITTER_IDENT, as returned by "git var -l". the value of the 'sendemail.from' configuration option is used. If
The user will still be prompted to confirm this entry. neither the command line option nor 'sendemail.from' are set, then the
user will be prompted for the value. The default for the prompt will be
the value of GIT_AUTHOR_IDENT, or GIT_COMMITTER_IDENT if that is not
set, as returned by "git var -l".
--in-reply-to:: --in-reply-to=<identifier>::
Specify the contents of the first In-Reply-To header. Specify the contents of the first In-Reply-To header.
Subsequent emails will refer to the previous email Subsequent emails will refer to the previous email
instead of this if --chain-reply-to is set (the default) instead of this if --chain-reply-to is set (the default)
Only necessary if --compose is also set. If --compose Only necessary if --compose is also set. If --compose
is not set, this will be prompted for. is not set, this will be prompted for.
--subject:: --subject=<string>::
Specify the initial subject of the email thread. Specify the initial subject of the email thread.
Only necessary if --compose is also set. If --compose Only necessary if --compose is also set. If --compose
is not set, this will be prompted for. is not set, this will be prompted for.
--to:: --to=<address>::
Specify the primary recipient of the emails generated. Generally, this Specify the primary recipient of the emails generated. Generally, this
will be the upstream maintainer of the project involved. Default is the will be the upstream maintainer of the project involved. Default is the
value of the 'sendemail.to' configuration value; if that is unspecified, value of the 'sendemail.to' configuration value; if that is unspecified,
@ -98,7 +100,7 @@ The --to option must be repeated for each user you want on the to list.
Sending Sending
~~~~~~~ ~~~~~~~
--envelope-sender:: --envelope-sender=<address>::
Specify the envelope sender used to send the emails. Specify the envelope sender used to send the emails.
This is useful if your default address is not the address that is This is useful if your default address is not the address that is
subscribed to a list. If you use the sendmail binary, you must have subscribed to a list. If you use the sendmail binary, you must have
@ -106,12 +108,12 @@ Sending
the 'sendemail.envelopesender' configuration variable; if that is the 'sendemail.envelopesender' configuration variable; if that is
unspecified, choosing the envelope sender is left to your MTA. unspecified, choosing the envelope sender is left to your MTA.
--smtp-encryption:: --smtp-encryption=<encryption>::
Specify the encryption to use, either 'ssl' or 'tls'. Any other Specify the encryption to use, either 'ssl' or 'tls'. Any other
value reverts to plain SMTP. Default is the value of value reverts to plain SMTP. Default is the value of
'sendemail.smtpencryption'. 'sendemail.smtpencryption'.
--smtp-pass:: --smtp-pass[=<password>]::
Password for SMTP-AUTH. The argument is optional: If no Password for SMTP-AUTH. The argument is optional: If no
argument is specified, then the empty string is used as argument is specified, then the empty string is used as
the password. Default is the value of 'sendemail.smtppass', the password. Default is the value of 'sendemail.smtppass',
@ -123,7 +125,7 @@ or on the command line. If a username has been specified (with
specified (with '--smtp-pass' or 'sendemail.smtppass'), then the specified (with '--smtp-pass' or 'sendemail.smtppass'), then the
user is prompted for a password while the input is masked for privacy. user is prompted for a password while the input is masked for privacy.
--smtp-server:: --smtp-server=<host>::
If set, specifies the outgoing SMTP server to use (e.g. If set, specifies the outgoing SMTP server to use (e.g.
`smtp.example.com` or a raw IP address). Alternatively it can `smtp.example.com` or a raw IP address). Alternatively it can
specify a full pathname of a sendmail-like program instead; specify a full pathname of a sendmail-like program instead;
@ -133,7 +135,7 @@ user is prompted for a password while the input is masked for privacy.
`/usr/lib/sendmail` if such program is available, or `/usr/lib/sendmail` if such program is available, or
`localhost` otherwise. `localhost` otherwise.
--smtp-server-port:: --smtp-server-port=<port>::
Specifies a port different from the default port (SMTP Specifies a port different from the default port (SMTP
servers typically listen to smtp port 25 and ssmtp port servers typically listen to smtp port 25 and ssmtp port
465). This can be set with 'sendemail.smtpserverport'. 465). This can be set with 'sendemail.smtpserverport'.
@ -141,7 +143,7 @@ user is prompted for a password while the input is masked for privacy.
--smtp-ssl:: --smtp-ssl::
Legacy alias for '--smtp-encryption ssl'. Legacy alias for '--smtp-encryption ssl'.
--smtp-user:: --smtp-user=<user>::
Username for SMTP-AUTH. Default is the value of 'sendemail.smtpuser'; Username for SMTP-AUTH. Default is the value of 'sendemail.smtpuser';
if a username is not specified (with '--smtp-user' or 'sendemail.smtpuser'), if a username is not specified (with '--smtp-user' or 'sendemail.smtpuser'),
then authentication is not attempted. then authentication is not attempted.
@ -150,13 +152,13 @@ user is prompted for a password while the input is masked for privacy.
Automating Automating
~~~~~~~~~~ ~~~~~~~~~~
--cc-cmd:: --cc-cmd=<command>::
Specify a command to execute once per patch file which Specify a command to execute once per patch file which
should generate patch file specific "Cc:" entries. should generate patch file specific "Cc:" entries.
Output of this command must be single email address per line. Output of this command must be single email address per line.
Default is the value of 'sendemail.cccmd' configuration value. Default is the value of 'sendemail.cccmd' configuration value.
--[no-]chain-reply-to:: --[no-]chain-reply-to=<identifier>::
If this is set, each email will be sent as a reply to the previous If this is set, each email will be sent as a reply to the previous
email sent. If disabled with "--no-chain-reply-to", all emails after email sent. If disabled with "--no-chain-reply-to", all emails after
the first will be sent as replies to the first email sent. When using the first will be sent as replies to the first email sent. When using
@ -164,7 +166,7 @@ Automating
entire patch series. Default is the value of the 'sendemail.chainreplyto' entire patch series. Default is the value of the 'sendemail.chainreplyto'
configuration value; if that is unspecified, default to --chain-reply-to. configuration value; if that is unspecified, default to --chain-reply-to.
--identity:: --identity=<identity>::
A configuration identity. When given, causes values in the A configuration identity. When given, causes values in the
'sendemail.<identity>' subsection to take precedence over 'sendemail.<identity>' subsection to take precedence over
values in the 'sendemail' section. The default identity is values in the 'sendemail' section. The default identity is
@ -175,7 +177,7 @@ Automating
cc list. Default is the value of 'sendemail.signedoffbycc' configuration cc list. Default is the value of 'sendemail.signedoffbycc' configuration
value; if that is unspecified, default to --signed-off-by-cc. value; if that is unspecified, default to --signed-off-by-cc.
--suppress-cc:: --suppress-cc=<category>::
Specify an additional category of recipients to suppress the Specify an additional category of recipients to suppress the
auto-cc of: auto-cc of:
+ +
@ -212,6 +214,22 @@ specified, as well as 'body' if --no-signed-off-cc is specified.
Administering Administering
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
--confirm=<mode>::
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:: --dry-run::
Do everything except actually send the emails. Do everything except actually send the emails.
@ -247,7 +265,7 @@ sendemail.aliasesfile::
sendemail.aliasfiletype:: sendemail.aliasfiletype::
Format of the file(s) specified in sendemail.aliasesfile. Must be Format of the file(s) specified in sendemail.aliasesfile. Must be
one of 'mutt', 'mailrc', 'pine', or 'gnus'. one of 'mutt', 'mailrc', 'pine', 'elm', or 'gnus'.
sendemail.multiedit:: sendemail.multiedit::
If true (default), a single editor instance will be spawned to edit If true (default), a single editor instance will be spawned to edit
@ -255,6 +273,11 @@ sendemail.multiedit::
summary when '--compose' is used). If false, files will be edited one summary when '--compose' is used). If false, files will be edited one
after the other, spawning a new editor each time. 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 Author
------ ------

Просмотреть файл

@ -18,9 +18,9 @@ of server-side GIT commands implementing the pull/push functionality.
The commands can be executed only by the '-c' option; the shell is not The commands can be executed only by the '-c' option; the shell is not
interactive. interactive.
Currently, only three commands are permitted to be called, 'git-receive-pack' Currently, only four commands are permitted to be called, 'git-receive-pack'
'git-upload-pack' with a single required argument or 'cvs server' (to invoke 'git-upload-pack' and 'git-upload-archive' with a single required argument, or
'git-cvsserver'). 'cvs server' (to invoke 'git-cvsserver').
Author Author
------ ------

Просмотреть файл

@ -10,6 +10,7 @@ SYNOPSIS
[verse] [verse]
'git show-branch' [--all] [--remotes] [--topo-order] [--current] 'git show-branch' [--all] [--remotes] [--topo-order] [--current]
[--more=<n> | --list | --independent | --merge-base] [--more=<n> | --list | --independent | --merge-base]
[--color | --no-color]
[--no-name | --sha1-name] [--topics] [<rev> | <glob>]... [--no-name | --sha1-name] [--topics] [<rev> | <glob>]...
'git show-branch' (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>] 'git show-branch' (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]
@ -107,6 +108,14 @@ OPTIONS
When no explicit <ref> parameter is given, it defaults to the When no explicit <ref> parameter is given, it defaults to the
current branch (or `HEAD` if it is detached). current branch (or `HEAD` if it is detached).
--color::
Color the status sign (one of these: `*` `!` `+` `-`) of each commit
corresponding to the branch it's in.
--no-color::
Turn off colored output, even when the configuration file gives the
default to color output.
Note that --more, --list, --independent and --merge-base options Note that --more, --list, --independent and --merge-base options
are mutually exclusive. are mutually exclusive.
@ -148,9 +157,10 @@ $ git show-branch master fixes mhf
------------------------------------------------ ------------------------------------------------
These three branches all forked from a common commit, [master], These three branches all forked from a common commit, [master],
whose commit message is "Add 'git show-branch'. "fixes" branch whose commit message is "Add \'git show-branch\'". The "fixes"
adds one commit 'Introduce "reset type"'. "mhf" branch has many branch adds one commit "Introduce "reset type" flag to "git reset"".
other commits. The current branch is "master". The "mhf" branch adds many other commits. The current branch
is "master".
EXAMPLE EXAMPLE

Просмотреть файл

@ -9,10 +9,12 @@ git-submodule - Initialize, update or inspect submodules
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git submodule' [--quiet] add [-b branch] [--] <repository> <path> 'git submodule' [--quiet] add [-b branch]
[--reference <repository>] [--] <repository> <path>
'git submodule' [--quiet] status [--cached] [--] [<path>...] 'git submodule' [--quiet] status [--cached] [--] [<path>...]
'git submodule' [--quiet] init [--] [<path>...] 'git submodule' [--quiet] init [--] [<path>...]
'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--] [<path>...] 'git submodule' [--quiet] update [--init] [-N|--no-fetch]
[--reference <repository>] [--] [<path>...]
'git submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...] 'git submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...]
'git submodule' [--quiet] foreach <command> 'git submodule' [--quiet] foreach <command>
'git submodule' [--quiet] sync [--] [<path>...] 'git submodule' [--quiet] sync [--] [<path>...]
@ -177,6 +179,14 @@ OPTIONS
This option is only valid for the update command. This option is only valid for the update command.
Don't fetch new objects from the remote site. Don't fetch new objects from the remote site.
--reference <repository>::
This option is only valid for add and update commands. These
commands sometimes need to clone a remote repository. In this case,
this option will be passed to the linkgit:git-clone[1] command.
+
*NOTE*: Do *not* use this option unless you have read the note
for linkgit:git-clone[1]'s --reference and --shared options carefully.
<path>...:: <path>...::
Paths to submodule(s). When specified this will restrict the command Paths to submodule(s). When specified this will restrict the command
to only operate on the submodules found at the specified paths. to only operate on the submodules found at the specified paths.

Просмотреть файл

@ -85,6 +85,10 @@ COMMANDS
specified, the prefix must include a trailing slash. specified, the prefix must include a trailing slash.
Setting a prefix is useful if you wish to track multiple Setting a prefix is useful if you wish to track multiple
projects that share a common repository. projects that share a common repository.
--ignore-paths=<regex>;;
When passed to 'init' or 'clone' this regular expression will
be preserved as a config key. See 'fetch' for a description
of '--ignore-paths'.
'fetch':: 'fetch'::
Fetch unfetched revisions from the Subversion remote we are Fetch unfetched revisions from the Subversion remote we are
@ -97,6 +101,9 @@ COMMANDS
makes 'git-log' (even without --date=local) show the same times makes 'git-log' (even without --date=local) show the same times
that `svn log` would in the local timezone. that `svn log` would in the local timezone.
--parent;;
Fetch only from the SVN parent of the current HEAD.
This doesn't interfere with interoperating with the Subversion This doesn't interfere with interoperating with the Subversion
repository you cloned from, but if you wish for your local Git repository you cloned from, but if you wish for your local Git
repository to be able to interoperate with someone else's local Git repository to be able to interoperate with someone else's local Git
@ -104,17 +111,25 @@ repository, either don't use this option or you should both use it in
the same local timezone. the same local timezone.
--ignore-paths=<regex>;; --ignore-paths=<regex>;;
This allows one to specify Perl regular expression that will This allows one to specify a Perl regular expression that will
cause skipping of all matching paths from checkout from SVN. cause skipping of all matching paths from checkout from SVN.
Examples: The '--ignore-paths' option should match for every 'fetch'
(including automatic fetches due to 'clone', 'dcommit',
'rebase', etc) on a given repository.
--ignore-paths="^doc" - skip "doc*" directory for every fetch. config key: svn-remote.<name>.ignore-paths
--ignore-paths="^[^/]+/(?:branches|tags)" - skip "branches" If the ignore-paths config key is set and the command
and "tags" of first level directories. line option is also given, both regular expressions
will be used.
Regular expression is not persistent, you should specify Examples:
it every time when fetching.
--ignore-paths="^doc" - skip "doc*" directory for every
fetch.
--ignore-paths="^[^/]+/(?:branches|tags)" - skip
"branches" and "tags" of first level directories.
'clone':: 'clone'::
Runs 'init' and 'fetch'. It will automatically create a Runs 'init' and 'fetch'. It will automatically create a
@ -383,9 +398,18 @@ after the authors-file is modified should continue operation.
config key: svn.authorsfile config key: svn.authorsfile
--authors-prog=<filename>::
If this option is specified, for each SVN committer name that does not
exist in the authors file, the given file is executed with the committer
name as the first argument. The program is expected to return a single
line of the form "Name <email>", which will be treated as if included in
the authors file.
-q:: -q::
--quiet:: --quiet::
Make 'git-svn' less verbose. Make 'git-svn' less verbose. Specify a second time to make it
even less verbose.
--repack[=<n>]:: --repack[=<n>]::
--repack-flags=<flags>:: --repack-flags=<flags>::
@ -672,14 +696,14 @@ listed below are allowed:
------------------------------------------------------------------------ ------------------------------------------------------------------------
[svn-remote "project-a"] [svn-remote "project-a"]
url = http://server.org/svn url = http://server.org/svn
fetch = trunk/project-a:refs/remotes/project-a/trunk
branches = branches/*/project-a:refs/remotes/project-a/branches/* branches = branches/*/project-a:refs/remotes/project-a/branches/*
tags = tags/*/project-a:refs/remotes/project-a/tags/* 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 Keep in mind that the '\*' (asterisk) wildcard of the local ref
(right of the ':') *must* be the farthest right path component; (right of the ':') *must* be the farthest right path component;
however the remote wildcard may be anywhere as long as it's own however the remote wildcard may be anywhere as long as it's an
independent path component (surrounded by '/' or EOL). This independent path component (surrounded by '/' or EOL). This
type of configuration is not automatically created by 'init' and type of configuration is not automatically created by 'init' and
should be manually entered with a text-editor or using 'git-config'. should be manually entered with a text-editor or using 'git-config'.

Просмотреть файл

@ -39,12 +39,6 @@ what they are for:
* info/refs * info/refs
BUGS
----
When you remove an existing ref, the command fails to update
info/refs file unless `--force` flag is given.
Author Author
------ ------
Written by Junio C Hamano <gitster@pobox.com> Written by Junio C Hamano <gitster@pobox.com>

Просмотреть файл

@ -9,7 +9,7 @@ git - the stupid content tracker
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git' [--version] [--exec-path[=GIT_EXEC_PATH]] 'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]
[-p|--paginate|--no-pager] [-p|--paginate|--no-pager]
[--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]
[--help] COMMAND [ARGS] [--help] COMMAND [ARGS]
@ -43,9 +43,14 @@ unreleased) version of git, that is available from 'master'
branch of the `git.git` repository. branch of the `git.git` repository.
Documentation for older releases are available here: Documentation for older releases are available here:
* link:v1.6.2/git.html[documentation for release 1.6.2] * link:v1.6.3/git.html[documentation for release 1.6.3]
* release notes for * release notes for
link:RelNotes-1.6.2.5.txt[1.6.2.5],
link:RelNotes-1.6.2.4.txt[1.6.2.4],
link:RelNotes-1.6.2.3.txt[1.6.2.3],
link:RelNotes-1.6.2.2.txt[1.6.2.2],
link:RelNotes-1.6.2.1.txt[1.6.2.1],
link:RelNotes-1.6.2.txt[1.6.2]. link:RelNotes-1.6.2.txt[1.6.2].
* link:v1.6.1.3/git.html[documentation for release 1.6.1.3] * link:v1.6.1.3/git.html[documentation for release 1.6.1.3]
@ -177,6 +182,10 @@ help ...`.
environment variable. If no path is given, 'git' will print environment variable. If no path is given, 'git' will print
the current setting and then exit. the current setting and then exit.
--html-path::
Print the path to wherever your git HTML documentation is installed
and exit.
-p:: -p::
--paginate:: --paginate::
Pipe all output into 'less' (or if set, $PAGER). Pipe all output into 'less' (or if set, $PAGER).

Просмотреть файл

@ -46,20 +46,20 @@ Here are the rules regarding the "flags" that you should follow when you are
scripting git: scripting git:
* it's preferred to use the non dashed form of git commands, which means that * 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"` * splitting short options to separate words (prefer `git foo -a -b`
to `"git foo -ab"`, the latter may not even work). to `git foo -ab`, the latter may not even work).
* when a command line option takes an argument, use the 'sticked' form. In * 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 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"` 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 for long options. An option that takes optional option-argument must be
written in the 'sticked' form. written in the 'sticked' form.
* when you give a revision parameter to a command, make sure the parameter is * 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 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. if you happen to have a file called `HEAD` in the work tree.
@ -99,17 +99,17 @@ usage: git-describe [options] <committish>*
Negating options Negating options
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
Options with long option names can be negated by prefixing `"--no-"`. For 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 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"` can use `--no-track` to override that behaviour. The same goes for `--color`
and `"--no-color"`. and `--no-color`.
Aggregating short options Aggregating short options
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
Commands that support the enhanced option parser allow you to aggregate short 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 options. This means that you can for example use `git rm -rf` or
`"git clean -fdx"`. `git clean -fdx`.
Separating argument from the option Separating argument from the option

Просмотреть файл

@ -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). flag=1) or a file checkout (retrieving a file from the index, flag=0).
This hook cannot affect the outcome of 'git-checkout'. 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 This hook can be used to perform repository validity checks, auto-display
differences from the previous HEAD if different, or set working dir metadata differences from the previous HEAD if different, or set working dir metadata
properties. properties.

Просмотреть файл

@ -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 'origin' is used for that purpose. New upstream updates
will be fetched into remote <<def_tracking_branch,tracking branches>> named will be fetched into remote <<def_tracking_branch,tracking branches>> named
origin/name-of-upstream-branch, which you can see using origin/name-of-upstream-branch, which you can see using
"`git branch -r`". `git branch -r`.
[[def_pack]]pack:: [[def_pack]]pack::
A set of objects which have been compressed into one file (to save space A set of objects which have been compressed into one file (to save space
@ -449,6 +449,12 @@ This commit is referred to as a "merge commit", or sometimes just a
An <<def_object,object>> which is not <<def_reachable,reachable>> from a An <<def_object,object>> which is not <<def_reachable,reachable>> from a
<<def_branch,branch>>, <<def_tag,tag>>, or any other reference. <<def_branch,branch>>, <<def_tag,tag>>, or any other reference.
[[def_upstream_branch]]upstream branch::
The default <<def_branch,branch>> that is merged into the branch in
question (or the branch in question is rebased onto). It is configured
via branch.<name>.remote and branch.<name>.merge. If the upstream branch
of 'A' is 'origin/B' sometimes we say "'A' is tracking 'origin/B'".
[[def_working_tree]]working tree:: [[def_working_tree]]working tree::
The tree of actual checked out files. The working tree is The tree of actual checked out files. The working tree is
normally equal to the <<def_HEAD,HEAD>> plus any local changes normally equal to the <<def_HEAD,HEAD>> plus any local changes

Просмотреть файл

@ -1,21 +1,14 @@
<!-- Based on callouts.xsl. Fixes man page callouts for DocBook 1.72 XSL --> <!-- manpage-1.72.xsl:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 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:import href="manpage-base.xsl"/>
<xsl:param name="refentry.meta.get.quietly" select="1"/>
<xsl:template match="co"> <!-- these are the special values for the roff control characters
<xsl:value-of select="concat('&#x2593;fB(',substring-after(@id,'-'),')&#x2593;fR')"/> needed for docbook-xsl 1.72.0 -->
</xsl:template> <xsl:param name="git.docbook.backslash">&#x2593;</xsl:param>
<xsl:template match="calloutlist"> <xsl:param name="git.docbook.dot" >&#x2302;</xsl:param>
<xsl:text>&#x2302;sp&#10;</xsl:text>
<xsl:apply-templates/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="callout">
<xsl:value-of select="concat('&#x2593;fB',substring-after(@arearefs,'-'),'. &#x2593;fR')"/>
<xsl:apply-templates/>
<xsl:text>&#x2302;br&#10;</xsl:text>
</xsl:template>
</xsl:stylesheet> </xsl:stylesheet>

Просмотреть файл

@ -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&#10;</xsl:text>
<xsl:apply-templates/>
<xsl:text>&#10;</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&#10;</xsl:text>
</xsl:template>
</xsl:stylesheet>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>&#10;&#10;</xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

Просмотреть файл

@ -22,7 +22,8 @@ merge.stat::
merge.tool:: merge.tool::
Controls which merge resolution program is used by Controls which merge resolution program is used by
linkgit:git-mergetool[1]. Valid built-in values are: "kdiff3", linkgit:git-mergetool[1]. Valid built-in values are: "kdiff3",
"tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff",
"diffuse", "ecmerge", "tortoisemerge", and
"opendiff". Any other value is treated is custom merge tool "opendiff". Any other value is treated is custom merge tool
and there must be a corresponding mergetool.<tool>.cmd option. and there must be a corresponding mergetool.<tool>.cmd option.

Просмотреть файл

@ -3,15 +3,15 @@ MERGE STRATEGIES
resolve:: resolve::
This can only resolve two heads (i.e. the current branch 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 algorithm. It tries to carefully detect criss-cross
merge ambiguities and is considered generally safe and merge ambiguities and is considered generally safe and
fast. fast.
recursive:: recursive::
This can only resolve two heads using 3-way merge This can only resolve two heads using a 3-way merge
algorithm. When there are more than one common algorithm. When there is more than one common
ancestors that can be used for 3-way merge, it creates a ancestor that can be used for 3-way merge, it creates a
merged tree of the common ancestors and uses that as merged tree of the common ancestors and uses that as
the reference tree for the 3-way merge. This has been the reference tree for the 3-way merge. This has been
reported to result in fewer merge conflicts without reported to result in fewer merge conflicts without
@ -22,11 +22,11 @@ recursive::
pulling or merging one branch. pulling or merging one branch.
octopus:: octopus::
This resolves more than two-head case, but refuses to do This resolves cases with more than two heads, but refuses to do
complex merge that needs manual resolution. It is a complex merge that needs manual resolution. It is
primarily meant to be used for bundling topic branch primarily meant to be used for bundling topic branch
heads together. This is the default merge strategy when heads together. This is the default merge strategy when
pulling or merging more than one branches. pulling or merging more than one branch.
ours:: ours::
This resolves any number of heads, but the result of the This resolves any number of heads, but the result of the

Просмотреть файл

@ -121,6 +121,7 @@ The placeholders are:
- '%d': ref names, like the --decorate option of linkgit:git-log[1] - '%d': ref names, like the --decorate option of linkgit:git-log[1]
- '%e': encoding - '%e': encoding
- '%s': subject - '%s': subject
- '%f': sanitized subject line, suitable for a filename
- '%b': body - '%b': body
- '%Cred': switch color to red - '%Cred': switch color to red
- '%Cgreen': switch color to green - '%Cgreen': switch color to green
@ -152,3 +153,12 @@ $ git log -2 --pretty=tformat:%h 4da45bef \
4da45be 4da45be
7134973 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
---------------------

Просмотреть файл

@ -1,4 +1,5 @@
--pretty[='<format>']:: --pretty[='<format>']::
--format[='<format>']::
Pretty-print the contents of the commit logs in a given format, Pretty-print the contents of the commit logs in a given format,
where '<format>' can be one of 'oneline', 'short', 'medium', 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 This should make "--pretty=oneline" a whole lot more readable for
people using 80-column terminals. people using 80-column terminals.
--oneline::
This is a shorthand for "--pretty=oneline --abbrev-commit"
used together.
--encoding[=<encoding>]:: --encoding[=<encoding>]::
The commit objects record the encoding used for the log message The commit objects record the encoding used for the log message
in their encoding header; this option can be used to tell the in their encoding header; this option can be used to tell the

Просмотреть файл

@ -140,38 +140,38 @@ limiting may be applied.
-- --
-n 'number':: -n 'number'::
--max-count='number':: --max-count=<number>::
Limit the number of commits output. Limit the number of commits output.
--skip='number':: --skip=<number>::
Skip 'number' commits before starting to show the commit output. Skip 'number' commits before starting to show the commit output.
--since='date':: --since=<date>::
--after='date':: --after=<date>::
Show commits more recent than a specific date. Show commits more recent than a specific date.
--until='date':: --until=<date>::
--before='date':: --before=<date>::
Show commits older than a specific date. Show commits older than a specific date.
ifdef::git-rev-list[] ifdef::git-rev-list[]
--max-age='timestamp':: --max-age=<timestamp>::
--min-age='timestamp':: --min-age=<timestamp>::
Limit the commits output to specified time range. Limit the commits output to specified time range.
endif::git-rev-list[] endif::git-rev-list[]
--author='pattern':: --author=<pattern>::
--committer='pattern':: --committer=<pattern>::
Limit the commits output to ones with author/committer Limit the commits output to ones with author/committer
header lines that match the specified pattern (regular expression). header lines that match the specified pattern (regular expression).
--grep='pattern':: --grep=<pattern>::
Limit the commits output to ones with log message that Limit the commits output to ones with log message that
matches the specified pattern (regular expression). matches the specified pattern (regular expression).

Просмотреть файл

@ -66,6 +66,12 @@ Steps to parse options
non-option arguments in `argv[]`. non-option arguments in `argv[]`.
`argc` is updated appropriately because of the assignment. `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: Flags are the bitwise-or of:
`PARSE_OPT_KEEP_DASHDASH`:: `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 Using this flag, processing is stopped at the first non-option
argument. 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 Data Structure
-------------- --------------
@ -109,6 +137,10 @@ There are some macros to easily define options:
Introduce a boolean option. Introduce a boolean option.
If used, `int_var` is bitwise-ored with `mask`. If used, `int_var` is bitwise-ored with `mask`.
`OPT_NEGBIT(short, long, &int_var, description, mask)`::
Introduce a boolean option.
If used, `int_var` is bitwise-anded with the inverted `mask`.
`OPT_SET_INT(short, long, &int_var, description, integer)`:: `OPT_SET_INT(short, long, &int_var, description, integer)`::
Introduce a boolean option. Introduce a boolean option.
If used, set `int_var` to `integer`. If used, set `int_var` to `integer`.
@ -138,6 +170,14 @@ There are some macros to easily define options:
`OPT_ARGUMENT(long, description)`:: `OPT_ARGUMENT(long, description)`::
Introduce a long-option argument that will be kept in `argv[]`. Introduce a long-option argument that will be kept in `argv[]`.
`OPT_NUMBER_CALLBACK(&var, description, func_ptr)`::
Recognize numerical options like -123 and feed the integer as
if it was an argument to the function given by `func_ptr`.
The result will be put into `var`. There can be only one such
option definition. It cannot be negated and it takes no
arguments. Short options that happen to be digits take
precedence over it.
The last element of the array must be `OPT_END()`. The last element of the array must be `OPT_END()`.
@ -170,7 +210,7 @@ The function must be defined in this form:
The callback mechanism is as follows: The callback mechanism is as follows:
* Inside `funct`, the only interesting member of the structure * Inside `func`, the only interesting member of the structure
given by `opt` is the void pointer `opt->value`. given by `opt` is the void pointer `opt->value`.
`\*opt->value` will be the value that is saved into `var`, if you `\*opt->value` will be the value that is saved into `var`, if you
use `OPT_CALLBACK()`. use `OPT_CALLBACK()`.

Просмотреть файл

@ -188,7 +188,7 @@ As you can see, a commit shows who made the latest change, what they
did, and why. did, and why.
Every commit has a 40-hexdigit id, sometimes called the "object name" or the Every commit has a 40-hexdigit id, sometimes called the "object name" or the
"SHA1 id", shown on the first line of the "git-show" output. You can usually "SHA-1 id", shown on the first line of the "git show" output. You can usually
refer to a commit by a shorter name, such as a tag or a branch name, but this refer to a commit by a shorter name, such as a tag or a branch name, but this
longer name can also be useful. Most importantly, it is a globally unique longer name can also be useful. Most importantly, it is a globally unique
name for this commit: so if you tell somebody else the object name (for name for this commit: so if you tell somebody else the object name (for
@ -307,7 +307,7 @@ ref: refs/heads/master
Examining an old version without creating a new branch Examining an old version without creating a new branch
------------------------------------------------------ ------------------------------------------------------
The git-checkout command normally expects a branch head, but will also The `git checkout` command normally expects a branch head, but will also
accept an arbitrary commit; for example, you can check out the commit accept an arbitrary commit; for example, you can check out the commit
referenced by a tag: referenced by a tag:
@ -320,7 +320,7 @@ If you want to create a new branch from this checkout, you may do so
HEAD is now at 427abfa... Linux v2.6.17 HEAD is now at 427abfa... Linux v2.6.17
------------------------------------------------ ------------------------------------------------
The HEAD then refers to the SHA1 of the commit instead of to a branch, The HEAD then refers to the SHA-1 of the commit instead of to a branch,
and git branch shows that you are no longer on a branch: and git branch shows that you are no longer on a branch:
------------------------------------------------ ------------------------------------------------
@ -400,7 +400,7 @@ references with the same shorthand name, see the "SPECIFYING
REVISIONS" section of linkgit:git-rev-parse[1]. REVISIONS" section of linkgit:git-rev-parse[1].
[[Updating-a-repository-With-git-fetch]] [[Updating-a-repository-With-git-fetch]]
Updating a repository with git-fetch Updating a repository with git fetch
------------------------------------ ------------------------------------
Eventually the developer cloned from will do additional work in her Eventually the developer cloned from will do additional work in her
@ -427,7 +427,7 @@ $ git fetch linux-nfs
------------------------------------------------- -------------------------------------------------
New remote-tracking branches will be stored under the shorthand name New remote-tracking branches will be stored under the shorthand name
that you gave "git-remote add", in this case linux-nfs: that you gave "git remote add", in this case linux-nfs:
------------------------------------------------- -------------------------------------------------
$ git branch -r $ git branch -r
@ -516,7 +516,7 @@ $ git bisect reset
to return you to the branch you were on before. to return you to the branch you were on before.
Note that the version which git-bisect checks out for you at each Note that the version which `git bisect` checks out for you at each
point is just a suggestion, and you're free to try a different point is just a suggestion, and you're free to try a different
version if you think it would be a good idea. For example, version if you think it would be a good idea. For example,
occasionally you may land on a commit that broke something unrelated; occasionally you may land on a commit that broke something unrelated;
@ -592,11 +592,11 @@ In addition to HEAD, there are several other special names for
commits: commits:
Merges (to be discussed later), as well as operations such as Merges (to be discussed later), as well as operations such as
git-reset, which change the currently checked-out commit, generally `git reset`, which change the currently checked-out commit, generally
set ORIG_HEAD to the value HEAD had before the current operation. set ORIG_HEAD to the value HEAD had before the current operation.
The git-fetch operation always stores the head of the last fetched The `git fetch` operation always stores the head of the last fetched
branch in FETCH_HEAD. For example, if you run git fetch without branch in FETCH_HEAD. For example, if you run `git fetch` without
specifying a local branch as the target of the operation specifying a local branch as the target of the operation
------------------------------------------------- -------------------------------------------------
@ -739,7 +739,7 @@ $ git log --pretty=oneline origin..mybranch | wc -l
------------------------------------------------- -------------------------------------------------
Alternatively, you may often see this sort of thing done with the Alternatively, you may often see this sort of thing done with the
lower-level command linkgit:git-rev-list[1], which just lists the SHA1's lower-level command linkgit:git-rev-list[1], which just lists the SHA-1's
of all the given commits: of all the given commits:
------------------------------------------------- -------------------------------------------------
@ -1073,9 +1073,9 @@ $ git diff
shows the difference between the working tree and the index file. shows the difference between the working tree and the index file.
Note that "git-add" always adds just the current contents of a file Note that "git add" always adds just the current contents of a file
to the index; further changes to the same file will be ignored unless to the index; further changes to the same file will be ignored unless
you run git-add on the file again. you run `git add` on the file again.
When you're ready, just run When you're ready, just run
@ -1136,10 +1136,10 @@ Ignoring files
A project will often generate files that you do 'not' want to track with git. 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 This typically includes files generated by a build process or temporary
backup files made by your editor. Of course, 'not' tracking files with git 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 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 add .` practically useless, and they keep showing up in the output of
"`git status`". `git status`.
You can tell git to ignore certain files by creating a file called .gitignore 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: in the top level of your working directory, with contents such as:
@ -1349,7 +1349,7 @@ $ git add file.txt
------------------------------------------------- -------------------------------------------------
the different stages of that file will be "collapsed", after which the different stages of that file will be "collapsed", after which
git-diff will (by default) no longer show diffs for that file. `git diff` will (by default) no longer show diffs for that file.
[[undoing-a-merge]] [[undoing-a-merge]]
Undoing a merge Undoing a merge
@ -1446,7 +1446,7 @@ Fixing a mistake by rewriting history
If the problematic commit is the most recent commit, and you have not If the problematic commit is the most recent commit, and you have not
yet made that commit public, then you may just yet made that commit public, then you may just
<<undoing-a-merge,destroy it using git-reset>>. <<undoing-a-merge,destroy it using `git reset`>>.
Alternatively, you Alternatively, you
can edit the working directory and update the index to fix your can edit the working directory and update the index to fix your
@ -1474,7 +1474,7 @@ Checking out an old version of a file
In the process of undoing a previous bad change, you may find it In the process of undoing a previous bad change, you may find it
useful to check out an older version of a particular file using useful to check out an older version of a particular file using
linkgit:git-checkout[1]. We've used git-checkout before to switch linkgit:git-checkout[1]. We've used `git checkout` before to switch
branches, but it has quite different behavior if it is given a path branches, but it has quite different behavior if it is given a path
name: the command name: the command
@ -1542,7 +1542,7 @@ $ git gc
------------------------------------------------- -------------------------------------------------
to recompress the archive. This can be very time-consuming, so to recompress the archive. This can be very time-consuming, so
you may prefer to run git-gc when you are not doing other work. you may prefer to run `git gc` when you are not doing other work.
[[ensuring-reliability]] [[ensuring-reliability]]
@ -1634,7 +1634,7 @@ In some situations the reflog may not be able to save you. For example,
suppose you delete a branch, then realize you need the history it suppose you delete a branch, then realize you need the history it
contained. The reflog is also deleted; however, if you have not yet contained. The reflog is also deleted; however, if you have not yet
pruned the repository, then you may still be able to find the lost pruned the repository, then you may still be able to find the lost
commits in the dangling objects that git-fsck reports. See commits in the dangling objects that `git fsck` reports. See
<<dangling-objects>> for the details. <<dangling-objects>> for the details.
------------------------------------------------- -------------------------------------------------
@ -1676,7 +1676,7 @@ Sharing development with others
=============================== ===============================
[[getting-updates-With-git-pull]] [[getting-updates-With-git-pull]]
Getting updates with git-pull Getting updates with git pull
----------------------------- -----------------------------
After you clone a repository and make a few changes of your own, you After you clone a repository and make a few changes of your own, you
@ -1722,7 +1722,7 @@ repository that you pulled from.
<<fast-forwards,fast forward>>; instead, your branch will just be <<fast-forwards,fast forward>>; instead, your branch will just be
updated to point to the latest commit from the upstream branch.) updated to point to the latest commit from the upstream branch.)
The git-pull command can also be given "." as the "remote" repository, The `git pull` command can also be given "." as the "remote" repository,
in which case it just merges in a branch from the current repository; so in which case it just merges in a branch from the current repository; so
the commands the commands
@ -1795,7 +1795,7 @@ Public git repositories
Another way to submit changes to a project is to tell the maintainer Another way to submit changes to a project is to tell the maintainer
of that project to pull the changes from your repository using of that project to pull the changes from your repository using
linkgit:git-pull[1]. In the section "<<getting-updates-With-git-pull, linkgit:git-pull[1]. In the section "<<getting-updates-With-git-pull,
Getting updates with git-pull>>" we described this as a way to get Getting updates with `git pull`>>" we described this as a way to get
updates from the "main" repository, but it works just as well in the updates from the "main" repository, but it works just as well in the
other direction. other direction.
@ -1847,7 +1847,7 @@ Setting up a public repository
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Assume your personal repository is in the directory ~/proj. We Assume your personal repository is in the directory ~/proj. We
first create a new clone of the repository and tell git-daemon that it first create a new clone of the repository and tell `git daemon` that it
is meant to be public: is meant to be public:
------------------------------------------------- -------------------------------------------------
@ -1878,10 +1878,10 @@ repository>>", below.
Otherwise, all you need to do is start linkgit:git-daemon[1]; it will Otherwise, all you need to do is start linkgit:git-daemon[1]; it will
listen on port 9418. By default, it will allow access to any directory listen on port 9418. By default, it will allow access to any directory
that looks like a git directory and contains the magic file that looks like a git directory and contains the magic file
git-daemon-export-ok. Passing some directory paths as git-daemon git-daemon-export-ok. Passing some directory paths as `git daemon`
arguments will further restrict the exports to those paths. arguments will further restrict the exports to those paths.
You can also run git-daemon as an inetd service; see the You can also run `git daemon` as an inetd service; see the
linkgit:git-daemon[1] man page for details. (See especially the linkgit:git-daemon[1] man page for details. (See especially the
examples section.) examples section.)
@ -1942,7 +1942,7 @@ or just
$ git push ssh://yourserver.com/~you/proj.git master $ git push ssh://yourserver.com/~you/proj.git master
------------------------------------------------- -------------------------------------------------
As with git-fetch, git-push will complain if this does not result in a As with `git fetch`, `git push` will complain if this does not result in a
<<fast-forwards,fast forward>>; see the following section for details on <<fast-forwards,fast forward>>; see the following section for details on
handling this case. handling this case.
@ -1952,7 +1952,7 @@ repository that has a checked-out working tree, but the working tree
will not be updated by the push. This may lead to unexpected results if will not be updated by the push. This may lead to unexpected results if
the branch you push to is the currently checked-out branch! the branch you push to is the currently checked-out branch!
As with git-fetch, you may also set up configuration options to As with `git fetch`, you may also set up configuration options to
save typing; so, for example, after save typing; so, for example, after
------------------------------------------------- -------------------------------------------------
@ -1988,13 +1988,13 @@ error: failed to push to 'ssh://yourserver.com/~you/proj.git'
This can happen, for example, if you: This can happen, for example, if you:
- use `git-reset --hard` to remove already-published commits, or - use `git reset --hard` to remove already-published commits, or
- use `git-commit --amend` to replace already-published commits - use `git commit --amend` to replace already-published commits
(as in <<fixing-a-mistake-by-rewriting-history>>), or (as in <<fixing-a-mistake-by-rewriting-history>>), or
- use `git-rebase` to rebase any already-published commits (as - use `git rebase` to rebase any already-published commits (as
in <<using-git-rebase>>). in <<using-git-rebase>>).
You may force git-push to perform the update anyway by preceding the You may force `git push` to perform the update anyway by preceding the
branch name with a plus sign: branch name with a plus sign:
------------------------------------------------- -------------------------------------------------
@ -2036,7 +2036,7 @@ advantages over the central shared repository:
- Git's ability to quickly import and merge patches allows a - Git's ability to quickly import and merge patches allows a
single maintainer to process incoming changes even at very single maintainer to process incoming changes even at very
high rates. And when that becomes too much, git-pull provides high rates. And when that becomes too much, `git pull` provides
an easy way for that maintainer to delegate this job to other an easy way for that maintainer to delegate this job to other
maintainers while still allowing optional review of incoming maintainers while still allowing optional review of incoming
changes. changes.
@ -2404,7 +2404,7 @@ use them, and then explain some of the problems that can arise because
you are rewriting history. you are rewriting history.
[[using-git-rebase]] [[using-git-rebase]]
Keeping a patch series up to date using git-rebase Keeping a patch series up to date using git rebase
-------------------------------------------------- --------------------------------------------------
Suppose that you create a branch "mywork" on a remote-tracking branch Suppose that you create a branch "mywork" on a remote-tracking branch
@ -2468,9 +2468,9 @@ patches to the new mywork. The result will look like:
................................................ ................................................
In the process, it may discover conflicts. In that case it will stop In the process, it may discover conflicts. In that case it will stop
and allow you to fix the conflicts; after fixing conflicts, use "git-add" and allow you to fix the conflicts; after fixing conflicts, use `git add`
to update the index with those contents, and then, instead of to update the index with those contents, and then, instead of
running git-commit, just run running `git commit`, just run
------------------------------------------------- -------------------------------------------------
$ git rebase --continue $ git rebase --continue
@ -2508,7 +2508,7 @@ with
$ git tag bad mywork~5 $ git tag bad mywork~5
------------------------------------------------- -------------------------------------------------
(Either gitk or git-log may be useful for finding the commit.) (Either gitk or `git log` may be useful for finding the commit.)
Then check out that commit, edit it, and rebase the rest of the series Then check out that commit, edit it, and rebase the rest of the series
on top of it (note that we could check out the commit on a temporary on top of it (note that we could check out the commit on a temporary
@ -2549,12 +2549,12 @@ $ gitk origin..mywork &
and browse through the list of patches in the mywork branch using gitk, and browse through the list of patches in the mywork branch using gitk,
applying them (possibly in a different order) to mywork-new using applying them (possibly in a different order) to mywork-new using
cherry-pick, and possibly modifying them as you go using `commit --amend`. cherry-pick, and possibly modifying them as you go using `git commit --amend`.
The linkgit:git-gui[1] command may also help as it allows you to The linkgit:git-gui[1] command may also help as it allows you to
individually select diff hunks for inclusion in the index (by individually select diff hunks for inclusion in the index (by
right-clicking on the diff hunk and choosing "Stage Hunk for Commit"). right-clicking on the diff hunk and choosing "Stage Hunk for Commit").
Another technique is to use git-format-patch to create a series of Another technique is to use `git format-patch` to create a series of
patches, then reset the state to before the patches: patches, then reset the state to before the patches:
------------------------------------------------- -------------------------------------------------
@ -2662,7 +2662,7 @@ you know is that D is bad, that Z is good, and that
linkgit:git-bisect[1] identifies C as the culprit, how will you linkgit:git-bisect[1] identifies C as the culprit, how will you
figure out that the problem is due to this change in semantics? figure out that the problem is due to this change in semantics?
When the result of a git-bisect is a non-merge commit, you should When the result of a `git bisect` is a non-merge commit, you should
normally be able to discover the problem by examining just that commit. normally be able to discover the problem by examining just that commit.
Developers can make this easy by breaking their changes into small Developers can make this easy by breaking their changes into small
self-contained commits. That won't help in the case above, however, self-contained commits. That won't help in the case above, however,
@ -2725,7 +2725,7 @@ master branch. In more detail:
git fetch and fast-forwards git fetch and fast-forwards
--------------------------- ---------------------------
In the previous example, when updating an existing branch, "git-fetch" In the previous example, when updating an existing branch, "git fetch"
checks to make sure that the most recent commit on the remote checks to make sure that the most recent commit on the remote
branch is a descendant of the most recent commit on your copy of the branch is a descendant of the most recent commit on your copy of the
branch before updating your copy of the branch to point at the new branch before updating your copy of the branch to point at the new
@ -2751,7 +2751,7 @@ resulting in a situation like:
o--o--o <-- new head of the branch o--o--o <-- new head of the branch
................................................ ................................................
In this case, "git-fetch" will fail, and print out a warning. In this case, "git fetch" will fail, and print out a warning.
In that case, you can still force git to update to the new head, as In that case, you can still force git to update to the new head, as
described in the following section. However, note that in the described in the following section. However, note that in the
@ -2760,7 +2760,7 @@ unless you've already created a reference of your own pointing to
them. them.
[[forcing-fetch]] [[forcing-fetch]]
Forcing git-fetch to do non-fast-forward updates Forcing git fetch to do non-fast-forward updates
------------------------------------------------ ------------------------------------------------
If git fetch fails because the new head of a branch is not a If git fetch fails because the new head of a branch is not a
@ -2865,8 +2865,8 @@ The Object Database
We already saw in <<understanding-commits>> that all commits are stored We already saw in <<understanding-commits>> that all commits are stored
under a 40-digit "object name". In fact, all the information needed to under a 40-digit "object name". In fact, all the information needed to
represent the history of a project is stored in objects with such names. represent the history of a project is stored in objects with such names.
In each case the name is calculated by taking the SHA1 hash of the In each case the name is calculated by taking the SHA-1 hash of the
contents of the object. The SHA1 hash is a cryptographic hash function. contents of the object. The SHA-1 hash is a cryptographic hash function.
What that means to us is that it is impossible to find two different What that means to us is that it is impossible to find two different
objects with the same name. This has a number of advantages; among objects with the same name. This has a number of advantages; among
others: others:
@ -2877,10 +2877,10 @@ others:
same content stored in two repositories will always be stored under same content stored in two repositories will always be stored under
the same name. the same name.
- Git can detect errors when it reads an object, by checking that the - Git can detect errors when it reads an object, by checking that the
object's name is still the SHA1 hash of its contents. object's name is still the SHA-1 hash of its contents.
(See <<object-details>> for the details of the object formatting and (See <<object-details>> for the details of the object formatting and
SHA1 calculation.) SHA-1 calculation.)
There are four different types of objects: "blob", "tree", "commit", and There are four different types of objects: "blob", "tree", "commit", and
"tag". "tag".
@ -2926,9 +2926,9 @@ committer Junio C Hamano <gitster@pobox.com> 1187591163 -0700
As you can see, a commit is defined by: As you can see, a commit is defined by:
- a tree: The SHA1 name of a tree object (as defined below), representing - a tree: The SHA-1 name of a tree object (as defined below), representing
the contents of a directory at a certain point in time. the contents of a directory at a certain point in time.
- parent(s): The SHA1 name of some number of commits which represent the - parent(s): The SHA-1 name of some number of commits which represent the
immediately previous step(s) in the history of the project. The immediately previous step(s) in the history of the project. The
example above has one parent; merge commits may have more than example above has one parent; merge commits may have more than
one. A commit with no parents is called a "root" commit, and one. A commit with no parents is called a "root" commit, and
@ -2977,13 +2977,13 @@ $ git ls-tree fb3a8bdd0ce
------------------------------------------------ ------------------------------------------------
As you can see, a tree object contains a list of entries, each with a As you can see, a tree object contains a list of entries, each with a
mode, object type, SHA1 name, and name, sorted by name. It represents mode, object type, SHA-1 name, and name, sorted by name. It represents
the contents of a single directory tree. the contents of a single directory tree.
The object type may be a blob, representing the contents of a file, or The object type may be a blob, representing the contents of a file, or
another tree, representing the contents of a subdirectory. Since trees another tree, representing the contents of a subdirectory. Since trees
and blobs, like all other objects, are named by the SHA1 hash of their and blobs, like all other objects, are named by the SHA-1 hash of their
contents, two trees have the same SHA1 name if and only if their contents, two trees have the same SHA-1 name if and only if their
contents (including, recursively, the contents of all subdirectories) contents (including, recursively, the contents of all subdirectories)
are identical. This allows git to quickly determine the differences are identical. This allows git to quickly determine the differences
between two related tree objects, since it can ignore any entries with between two related tree objects, since it can ignore any entries with
@ -3029,15 +3029,15 @@ currently checked out.
Trust Trust
~~~~~ ~~~~~
If you receive the SHA1 name of a blob from one source, and its contents If you receive the SHA-1 name of a blob from one source, and its contents
from another (possibly untrusted) source, you can still trust that those from another (possibly untrusted) source, you can still trust that those
contents are correct as long as the SHA1 name agrees. This is because contents are correct as long as the SHA-1 name agrees. This is because
the SHA1 is designed so that it is infeasible to find different contents the SHA-1 is designed so that it is infeasible to find different contents
that produce the same hash. that produce the same hash.
Similarly, you need only trust the SHA1 name of a top-level tree object Similarly, you need only trust the SHA-1 name of a top-level tree object
to trust the contents of the entire directory that it refers to, and if to trust the contents of the entire directory that it refers to, and if
you receive the SHA1 name of a commit from a trusted source, then you you receive the SHA-1 name of a commit from a trusted source, then you
can easily verify the entire history of commits reachable through can easily verify the entire history of commits reachable through
parents of that commit, and all of those contents of the trees referred parents of that commit, and all of those contents of the trees referred
to by those commits. to by those commits.
@ -3049,7 +3049,7 @@ that you trust that commit, and the immutability of the history of
commits tells others that they can trust the whole history. commits tells others that they can trust the whole history.
In other words, you can easily validate a whole archive by just In other words, you can easily validate a whole archive by just
sending out a single email that tells the people the name (SHA1 hash) sending out a single email that tells the people the name (SHA-1 hash)
of the top commit, and digitally sign that email using something of the top commit, and digitally sign that email using something
like GPG/PGP. like GPG/PGP.
@ -3090,7 +3090,7 @@ How git stores objects efficiently: pack files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Newly created objects are initially created in a file named after the Newly created objects are initially created in a file named after the
object's SHA1 hash (stored in .git/objects). object's SHA-1 hash (stored in .git/objects).
Unfortunately this system becomes inefficient once a project has a Unfortunately this system becomes inefficient once a project has a
lot of objects. Try this on an old project: lot of objects. Try this on an old project:
@ -3131,7 +3131,7 @@ $ git prune
to remove any of the "loose" objects that are now contained in the to remove any of the "loose" objects that are now contained in the
pack. This will also remove any unreferenced objects (which may be pack. This will also remove any unreferenced objects (which may be
created when, for example, you use "git-reset" to remove a commit). created when, for example, you use "git reset" to remove a commit).
You can verify that the loose objects are gone by looking at the You can verify that the loose objects are gone by looking at the
.git/objects directory or by running .git/objects directory or by running
@ -3160,7 +3160,7 @@ branch still exists, as does everything it pointed to. The branch
pointer itself just doesn't, since you replaced it with another one. pointer itself just doesn't, since you replaced it with another one.
There are also other situations that cause dangling objects. For There are also other situations that cause dangling objects. For
example, a "dangling blob" may arise because you did a "git-add" of a example, a "dangling blob" may arise because you did a "git add" of a
file, but then, before you actually committed it and made it part of the file, but then, before you actually committed it and made it part of the
bigger picture, you changed something else in that file and committed bigger picture, you changed something else in that file and committed
that *updated* thing--the old state that you added originally ends up that *updated* thing--the old state that you added originally ends up
@ -3210,7 +3210,7 @@ Usually, dangling blobs and trees aren't very interesting. They're
almost always the result of either being a half-way mergebase (the blob almost always the result of either being a half-way mergebase (the blob
will often even have the conflict markers from a merge in it, if you will often even have the conflict markers from a merge in it, if you
have had conflicting merges that you fixed up by hand), or simply have had conflicting merges that you fixed up by hand), or simply
because you interrupted a "git-fetch" with ^C or something like that, because you interrupted a "git fetch" with ^C or something like that,
leaving _some_ of the new objects in the object database, but just leaving _some_ of the new objects in the object database, but just
dangling and useless. dangling and useless.
@ -3225,9 +3225,9 @@ and they'll be gone. But you should only run "git prune" on a quiescent
repository--it's kind of like doing a filesystem fsck recovery: you repository--it's kind of like doing a filesystem fsck recovery: you
don't want to do that while the filesystem is mounted. don't want to do that while the filesystem is mounted.
(The same is true of "git-fsck" itself, btw, but since (The same is true of "git fsck" itself, btw, but since
git-fsck never actually *changes* the repository, it just reports `git fsck` never actually *changes* the repository, it just reports
on what it found, git-fsck itself is never "dangerous" to run. on what it found, `git fsck` itself is never 'dangerous' to run.
Running it while somebody is actually changing the repository can cause Running it while somebody is actually changing the repository can cause
confusing and scary messages, but it won't actually do anything bad. In confusing and scary messages, but it won't actually do anything bad. In
contrast, running "git prune" while somebody is actively changing the contrast, running "git prune" while somebody is actively changing the
@ -3297,7 +3297,7 @@ $ git hash-object -w somedirectory/myfile
------------------------------------------------ ------------------------------------------------
which will create and store a blob object with the contents of which will create and store a blob object with the contents of
somedirectory/myfile, and output the sha1 of that object. if you're somedirectory/myfile, and output the SHA-1 of that object. if you're
extremely lucky it might be 4b9458b3786228369c63936db65827de3cc06200, in extremely lucky it might be 4b9458b3786228369c63936db65827de3cc06200, in
which case you've guessed right, and the corruption is fixed! which case you've guessed right, and the corruption is fixed!
@ -3359,7 +3359,7 @@ The index
----------- -----------
The index is a binary file (generally kept in .git/index) containing a The index is a binary file (generally kept in .git/index) containing a
sorted list of path names, each with permissions and the SHA1 of a blob sorted list of path names, each with permissions and the SHA-1 of a blob
object; linkgit:git-ls-files[1] can show you the contents of the index: object; linkgit:git-ls-files[1] can show you the contents of the index:
------------------------------------------------- -------------------------------------------------
@ -3489,14 +3489,14 @@ done
NOTE: Do not use local URLs here if you plan to publish your superproject! NOTE: Do not use local URLs here if you plan to publish your superproject!
See what files `git-submodule` created: See what files `git submodule` created:
------------------------------------------------- -------------------------------------------------
$ ls -a $ ls -a
. .. .git .gitmodules a b c d . .. .git .gitmodules a b c d
------------------------------------------------- -------------------------------------------------
The `git-submodule add <repo> <path>` command does a couple of things: The `git submodule add <repo> <path>` command does a couple of things:
- It clones the submodule from <repo> to the given <path> under the - It clones the submodule from <repo> to the given <path> under the
current directory and by default checks out the master branch. current directory and by default checks out the master branch.
@ -3542,7 +3542,7 @@ init` to add the submodule repository URLs to `.git/config`:
$ git submodule init $ git submodule init
------------------------------------------------- -------------------------------------------------
Now use `git-submodule update` to clone the repositories and check out the Now use `git submodule update` to clone the repositories and check out the
commits specified in the superproject: commits specified in the superproject:
------------------------------------------------- -------------------------------------------------
@ -3552,8 +3552,8 @@ $ ls -a
. .. .git a.txt . .. .git a.txt
------------------------------------------------- -------------------------------------------------
One major difference between `git-submodule update` and `git-submodule add` is One major difference between `git submodule update` and `git submodule add` is
that `git-submodule update` checks out a specific commit, rather than the tip that `git submodule update` checks out a specific commit, rather than the tip
of a branch. It's like checking out a tag: the head is detached, so you're not of a branch. It's like checking out a tag: the head is detached, so you're not
working on a branch. working on a branch.
@ -3754,7 +3754,7 @@ unsaved state that you might want to restore later!) your current
index. Normal operation is just index. Normal operation is just
------------------------------------------------- -------------------------------------------------
$ git read-tree <sha1 of tree> $ git read-tree <SHA-1 of tree>
------------------------------------------------- -------------------------------------------------
and your index file will now be equivalent to the tree that you saved and your index file will now be equivalent to the tree that you saved
@ -3769,7 +3769,7 @@ You update your working directory from the index by "checking out"
files. This is not a very common operation, since normally you'd just files. This is not a very common operation, since normally you'd just
keep your files updated, and rather than write to your working keep your files updated, and rather than write to your working
directory, you'd tell the index files about the changes in your directory, you'd tell the index files about the changes in your
working directory (i.e. `git-update-index`). working directory (i.e. `git update-index`).
However, if you decide to jump to a new version, or check out somebody However, if you decide to jump to a new version, or check out somebody
else's version, or just restore a previous tree, you'd populate your else's version, or just restore a previous tree, you'd populate your
@ -3782,7 +3782,7 @@ $ git checkout-index filename
or, if you want to check out all of the index, use `-a`. or, if you want to check out all of the index, use `-a`.
NOTE! git-checkout-index normally refuses to overwrite old files, so NOTE! `git checkout-index` normally refuses to overwrite old files, so
if you have an old version of the tree already checked out, you will if you have an old version of the tree already checked out, you will
need to use the "-f" flag ('before' the "-a" flag or the filename) to need to use the "-f" flag ('before' the "-a" flag or the filename) to
'force' the checkout. 'force' the checkout.
@ -3820,7 +3820,7 @@ $ git commit-tree <tree> -p <parent> [-p <parent2> ..]
and then giving the reason for the commit on stdin (either through and then giving the reason for the commit on stdin (either through
redirection from a pipe or file, or by just typing it at the tty). redirection from a pipe or file, or by just typing it at the tty).
git-commit-tree will return the name of the object that represents `git commit-tree` will return the name of the object that represents
that commit, and you should save it away for later use. Normally, that commit, and you should save it away for later use. Normally,
you'd commit a new `HEAD` state, and while git doesn't care where you you'd commit a new `HEAD` state, and while git doesn't care where you
save the note about that state, in practice we tend to just write the save the note about that state, in practice we tend to just write the
@ -3889,7 +3889,7 @@ $ git cat-file blob|tree|commit|tag <objectname>
to show its contents. NOTE! Trees have binary content, and as a result to show its contents. NOTE! Trees have binary content, and as a result
there is a special helper for showing that content, called there is a special helper for showing that content, called
`git-ls-tree`, which turns the binary content into a more easily `git ls-tree`, which turns the binary content into a more easily
readable form. readable form.
It's especially instructive to look at "commit" objects, since those It's especially instructive to look at "commit" objects, since those
@ -3978,13 +3978,13 @@ $ git ls-files --unmerged
------------------------------------------------ ------------------------------------------------
Each line of the `git ls-files --unmerged` output begins with Each line of the `git ls-files --unmerged` output begins with
the blob mode bits, blob SHA1, 'stage number', and the the blob mode bits, blob SHA-1, 'stage number', and the
filename. The 'stage number' is git's way to say which tree it filename. The 'stage number' is git's way to say which tree it
came from: stage 1 corresponds to `$orig` tree, stage 2 `HEAD` came from: stage 1 corresponds to `$orig` tree, stage 2 `HEAD`
tree, and stage3 `$target` tree. tree, and stage3 `$target` tree.
Earlier we said that trivial merges are done inside Earlier we said that trivial merges are done inside
`git-read-tree -m`. For example, if the file did not change `git read-tree -m`. For example, if the file did not change
from `$orig` to `HEAD` nor `$target`, or if the file changed from `$orig` to `HEAD` nor `$target`, or if the file changed
from `$orig` to `HEAD` and `$orig` to `$target` the same way, from `$orig` to `HEAD` and `$orig` to `$target` the same way,
obviously the final outcome is what is in `HEAD`. What the obviously the final outcome is what is in `HEAD`. What the
@ -4011,20 +4011,20 @@ $ mv -f hello.c~2 hello.c
$ git update-index hello.c $ git update-index hello.c
------------------------------------------------- -------------------------------------------------
When a path is in the "unmerged" state, running `git-update-index` for When a path is in the "unmerged" state, running `git update-index` for
that path tells git to mark the path resolved. that path tells git to mark the path resolved.
The above is the description of a git merge at the lowest level, The above is the description of a git merge at the lowest level,
to help you understand what conceptually happens under the hood. to help you understand what conceptually happens under the hood.
In practice, nobody, not even git itself, runs `git-cat-file` three times In practice, nobody, not even git itself, runs `git cat-file` three times
for this. There is a `git-merge-index` program that extracts the for this. There is a `git merge-index` program that extracts the
stages to temporary files and calls a "merge" script on it: stages to temporary files and calls a "merge" script on it:
------------------------------------------------- -------------------------------------------------
$ git merge-index git-merge-one-file hello.c $ git merge-index git-merge-one-file hello.c
------------------------------------------------- -------------------------------------------------
and that is what higher level `git-merge -s resolve` is implemented with. and that is what higher level `git merge -s resolve` is implemented with.
[[hacking-git]] [[hacking-git]]
Hacking git Hacking git
@ -4045,12 +4045,12 @@ objects). There are currently four different object types: "blob",
Regardless of object type, all objects share the following Regardless of object type, all objects share the following
characteristics: they are all deflated with zlib, and have a header characteristics: they are all deflated with zlib, and have a header
that not only specifies their type, but also provides size information that not only specifies their type, but also provides size information
about the data in the object. It's worth noting that the SHA1 hash about the data in the object. It's worth noting that the SHA-1 hash
that is used to name the object is the hash of the original data that is used to name the object is the hash of the original data
plus this header, so `sha1sum` 'file' does not match the object name plus this header, so `sha1sum` 'file' does not match the object name
for 'file'. for 'file'.
(Historical note: in the dawn of the age of git the hash (Historical note: in the dawn of the age of git the hash
was the sha1 of the 'compressed' object.) was the SHA-1 of the 'compressed' object.)
As a result, the general consistency of an object can always be tested As a result, the general consistency of an object can always be tested
independently of the contents or the type of the object: all objects can independently of the contents or the type of the object: all objects can
@ -4061,7 +4061,7 @@ size> {plus} <byte\0> {plus} <binary object data>.
The structured objects can further have their structure and The structured objects can further have their structure and
connectivity to other objects verified. This is generally done with connectivity to other objects verified. This is generally done with
the `git-fsck` program, which generates a full dependency graph the `git fsck` program, which generates a full dependency graph
of all objects, and verifies their internal consistency (in addition of all objects, and verifies their internal consistency (in addition
to just verifying their superficial consistency through the hash). to just verifying their superficial consistency through the hash).
@ -4120,7 +4120,7 @@ functions like `get_sha1_basic()` or the likes.
This is just to get you into the groove for the most libified part of Git: This is just to get you into the groove for the most libified part of Git:
the revision walker. the revision walker.
Basically, the initial version of `git-log` was a shell script: Basically, the initial version of `git log` was a shell script:
---------------------------------------------------------------- ----------------------------------------------------------------
$ git-rev-list --pretty $(git-rev-parse --default HEAD "$@") | \ $ git-rev-list --pretty $(git-rev-parse --default HEAD "$@") | \
@ -4129,20 +4129,20 @@ $ git-rev-list --pretty $(git-rev-parse --default HEAD "$@") | \
What does this mean? What does this mean?
`git-rev-list` is the original version of the revision walker, which `git rev-list` is the original version of the revision walker, which
_always_ printed a list of revisions to stdout. It is still functional, _always_ printed a list of revisions to stdout. It is still functional,
and needs to, since most new Git programs start out as scripts using and needs to, since most new Git programs start out as scripts using
`git-rev-list`. `git rev-list`.
`git-rev-parse` is not as important any more; it was only used to filter out `git rev-parse` is not as important any more; it was only used to filter out
options that were relevant for the different plumbing commands that were options that were relevant for the different plumbing commands that were
called by the script. called by the script.
Most of what `git-rev-list` did is contained in `revision.c` and Most of what `git rev-list` did is contained in `revision.c` and
`revision.h`. It wraps the options in a struct named `rev_info`, which `revision.h`. It wraps the options in a struct named `rev_info`, which
controls how and what revisions are walked, and more. controls how and what revisions are walked, and more.
The original job of `git-rev-parse` is now taken by the function The original job of `git rev-parse` is now taken by the function
`setup_revisions()`, which parses the revisions and the common command line `setup_revisions()`, which parses the revisions and the common command line
options for the revision walker. This information is stored in the struct options for the revision walker. This information is stored in the struct
`rev_info` for later consumption. You can do your own command line option `rev_info` for later consumption. You can do your own command line option
@ -4155,7 +4155,7 @@ just have a look at the first implementation of `cmd_log()`; call
`git show v1.3.0{tilde}155^2{tilde}4` and scroll down to that function (note that you `git show v1.3.0{tilde}155^2{tilde}4` and scroll down to that function (note that you
no longer need to call `setup_pager()` directly). no longer need to call `setup_pager()` directly).
Nowadays, `git-log` is a builtin, which means that it is _contained_ in the Nowadays, `git log` is a builtin, which means that it is _contained_ in the
command `git`. The source side of a builtin is command `git`. The source side of a builtin is
- a function called `cmd_<bla>`, typically defined in `builtin-<bla>.c`, - a function called `cmd_<bla>`, typically defined in `builtin-<bla>.c`,
@ -4171,7 +4171,7 @@ since they share quite a bit of code. In that case, the commands which are
_not_ named like the `.c` file in which they live have to be listed in _not_ named like the `.c` file in which they live have to be listed in
`BUILT_INS` in the `Makefile`. `BUILT_INS` in the `Makefile`.
`git-log` looks more complicated in C than it does in the original script, `git log` looks more complicated in C than it does in the original script,
but that allows for a much greater flexibility and performance. but that allows for a much greater flexibility and performance.
Here again it is a good point to take a pause. Here again it is a good point to take a pause.
@ -4182,9 +4182,9 @@ the organization of Git (after you know the basic concepts).
So, think about something which you are interested in, say, "how can I So, think about something which you are interested in, say, "how can I
access a blob just knowing the object name of it?". The first step is to access a blob just knowing the object name of it?". The first step is to
find a Git command with which you can do it. In this example, it is either find a Git command with which you can do it. In this example, it is either
`git-show` or `git-cat-file`. `git show` or `git cat-file`.
For the sake of clarity, let's stay with `git-cat-file`, because it For the sake of clarity, let's stay with `git cat-file`, because it
- is plumbing, and - is plumbing, and
@ -4198,7 +4198,7 @@ it does.
------------------------------------------------------------------ ------------------------------------------------------------------
git_config(git_default_config); git_config(git_default_config);
if (argc != 3) if (argc != 3)
usage("git-cat-file [-t|-s|-e|-p|<type>] <sha1>"); usage("git cat-file [-t|-s|-e|-p|<type>] <sha1>");
if (get_sha1(argv[2], sha1)) if (get_sha1(argv[2], sha1))
die("Not a valid object name %s", argv[2]); die("Not a valid object name %s", argv[2]);
------------------------------------------------------------------ ------------------------------------------------------------------
@ -4243,10 +4243,10 @@ To find out how the result can be used, just read on in `cmd_cat_file()`:
----------------------------------- -----------------------------------
Sometimes, you do not know where to look for a feature. In many such cases, Sometimes, you do not know where to look for a feature. In many such cases,
it helps to search through the output of `git log`, and then `git-show` the it helps to search through the output of `git log`, and then `git show` the
corresponding commit. corresponding commit.
Example: If you know that there was some test case for `git-bundle`, but Example: If you know that there was some test case for `git bundle`, but
do not remember where it was (yes, you _could_ `git grep bundle t/`, but that do not remember where it was (yes, you _could_ `git grep bundle t/`, but that
does not illustrate the point!): does not illustrate the point!):
@ -4530,7 +4530,7 @@ The basic requirements:
- Whenever possible, section headings should clearly describe the task - Whenever possible, section headings should clearly describe the task
they explain how to do, in language that requires no more knowledge they explain how to do, in language that requires no more knowledge
than necessary: for example, "importing patches into a project" rather than necessary: for example, "importing patches into a project" rather
than "the git-am command" than "the `git am` command"
Think about how to create a clear chapter dependency graph that will Think about how to create a clear chapter dependency graph that will
allow people to get to important topics without necessarily reading allow people to get to important topics without necessarily reading

Просмотреть файл

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
GVF=GIT-VERSION-FILE GVF=GIT-VERSION-FILE
DEF_VER=v1.6.2.5 DEF_VER=v1.6.3.GIT
LF=' LF='
' '

129
Makefile
Просмотреть файл

@ -126,6 +126,12 @@ all::
# randomly break unless your underlying filesystem supports those sub-second # randomly break unless your underlying filesystem supports those sub-second
# times (my ext3 doesn't). # 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 # 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. # change being considered an inode change from the update-index perspective.
# #
@ -139,6 +145,8 @@ all::
# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
# MakeMaker (e.g. using ActiveState under Cygwin). # MakeMaker (e.g. using ActiveState under Cygwin).
# #
# Define NO_PERL if you do not want Perl scripts or libraries at all.
#
# Define NO_TCLTK if you do not want Tcl/Tk GUI. # Define NO_TCLTK if you do not want Tcl/Tk GUI.
# #
# The TCL_PATH variable governs the location of the Tcl interpreter # The TCL_PATH variable governs the location of the Tcl interpreter
@ -159,6 +167,17 @@ all::
# Define NO_EXTERNAL_GREP if you don't want "git grep" to ever call # Define NO_EXTERNAL_GREP if you don't want "git grep" to ever call
# your external grep (e.g., if your system lacks grep, if its grep is # your external grep (e.g., if your system lacks grep, if its grep is
# broken, or spawning external process is slower than built-in grep git has). # broken, or spawning external process is slower than built-in grep git has).
#
# Define UNRELIABLE_FSTAT if your system's fstat does not return the same
# information on a not yet closed file that lstat would return for the same
# file after it was closed.
#
# Define OBJECT_CREATION_USES_RENAMES if your operating systems has problems
# when hardlinking a file to another name and unlinking the original file right
# away (some NTFS drivers seem to zero the contents in that scenario).
#
# Define NO_CROSS_DIRECTORY_HARDLINKS if you plan to distribute the installed
# programs as a tar, where bin/ and libexec/ might be on different file systems.
GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
@$(SHELL_PATH) ./GIT-VERSION-GEN @$(SHELL_PATH) ./GIT-VERSION-GEN
@ -209,6 +228,7 @@ ETC_GITCONFIG = etc/gitconfig
endif endif
lib = lib lib = lib
# DESTDIR= # DESTDIR=
pathsep = :
# default configuration for gitweb # default configuration for gitweb
GITWEB_CONFIG = gitweb_config.perl GITWEB_CONFIG = gitweb_config.perl
@ -257,14 +277,28 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
BASIC_CFLAGS = BASIC_CFLAGS =
BASIC_LDFLAGS = 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-am.sh
SCRIPT_SH += git-bisect.sh SCRIPT_SH += git-bisect.sh
SCRIPT_SH += git-difftool--helper.sh
SCRIPT_SH += git-filter-branch.sh SCRIPT_SH += git-filter-branch.sh
SCRIPT_SH += git-lost-found.sh SCRIPT_SH += git-lost-found.sh
SCRIPT_SH += git-merge-octopus.sh SCRIPT_SH += git-merge-octopus.sh
SCRIPT_SH += git-merge-one-file.sh SCRIPT_SH += git-merge-one-file.sh
SCRIPT_SH += git-merge-resolve.sh SCRIPT_SH += git-merge-resolve.sh
SCRIPT_SH += git-mergetool.sh SCRIPT_SH += git-mergetool.sh
SCRIPT_SH += git-mergetool--lib.sh
SCRIPT_SH += git-parse-remote.sh SCRIPT_SH += git-parse-remote.sh
SCRIPT_SH += git-pull.sh SCRIPT_SH += git-pull.sh
SCRIPT_SH += git-quiltimport.sh SCRIPT_SH += git-quiltimport.sh
@ -278,6 +312,7 @@ SCRIPT_SH += git-submodule.sh
SCRIPT_SH += git-web--browse.sh SCRIPT_SH += git-web--browse.sh
SCRIPT_PERL += git-add--interactive.perl SCRIPT_PERL += git-add--interactive.perl
SCRIPT_PERL += git-difftool.perl
SCRIPT_PERL += git-archimport.perl SCRIPT_PERL += git-archimport.perl
SCRIPT_PERL += git-cvsexportcommit.perl SCRIPT_PERL += git-cvsexportcommit.perl
SCRIPT_PERL += git-cvsimport.perl SCRIPT_PERL += git-cvsimport.perl
@ -333,7 +368,7 @@ BUILT_INS += git-whatchanged$X
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
# what 'all' will build but not install in gitexecdir # what 'all' will build but not install in gitexecdir
OTHER_PROGRAMS = git$X gitweb/gitweb.cgi OTHER_PROGRAMS = git$X
# Set paths to tools early so that they can be used for version tests. # Set paths to tools early so that they can be used for version tests.
ifndef SHELL_PATH ifndef SHELL_PATH
@ -412,6 +447,7 @@ LIB_OBJS += archive-tar.o
LIB_OBJS += archive-zip.o LIB_OBJS += archive-zip.o
LIB_OBJS += attr.o LIB_OBJS += attr.o
LIB_OBJS += base85.o LIB_OBJS += base85.o
LIB_OBJS += bisect.o
LIB_OBJS += blob.o LIB_OBJS += blob.o
LIB_OBJS += branch.o LIB_OBJS += branch.o
LIB_OBJS += bundle.o LIB_OBJS += bundle.o
@ -512,6 +548,7 @@ BUILTIN_OBJS += builtin-add.o
BUILTIN_OBJS += builtin-annotate.o BUILTIN_OBJS += builtin-annotate.o
BUILTIN_OBJS += builtin-apply.o BUILTIN_OBJS += builtin-apply.o
BUILTIN_OBJS += builtin-archive.o BUILTIN_OBJS += builtin-archive.o
BUILTIN_OBJS += builtin-bisect--helper.o
BUILTIN_OBJS += builtin-blame.o BUILTIN_OBJS += builtin-blame.o
BUILTIN_OBJS += builtin-branch.o BUILTIN_OBJS += builtin-branch.o
BUILTIN_OBJS += builtin-bundle.o BUILTIN_OBJS += builtin-bundle.o
@ -655,6 +692,7 @@ ifeq ($(uname_S),Darwin)
endif endif
NO_MEMMEM = YesPlease NO_MEMMEM = YesPlease
THREADED_DELTA_SEARCH = YesPlease THREADED_DELTA_SEARCH = YesPlease
USE_ST_TIMESPEC = YesPlease
endif endif
ifeq ($(uname_S),SunOS) ifeq ($(uname_S),SunOS)
NEEDS_SOCKET = YesPlease NEEDS_SOCKET = YesPlease
@ -704,6 +742,7 @@ ifeq ($(uname_S),FreeBSD)
BASIC_CFLAGS += -I/usr/local/include BASIC_CFLAGS += -I/usr/local/include
BASIC_LDFLAGS += -L/usr/local/lib BASIC_LDFLAGS += -L/usr/local/lib
DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
USE_ST_TIMESPEC = YesPlease
THREADED_DELTA_SEARCH = YesPlease THREADED_DELTA_SEARCH = YesPlease
ifeq ($(shell expr "$(uname_R)" : '4\.'),2) ifeq ($(shell expr "$(uname_R)" : '4\.'),2)
PTHREAD_LIBS = -pthread PTHREAD_LIBS = -pthread
@ -714,6 +753,7 @@ endif
ifeq ($(uname_S),OpenBSD) ifeq ($(uname_S),OpenBSD)
NO_STRCASESTR = YesPlease NO_STRCASESTR = YesPlease
NO_MEMMEM = YesPlease NO_MEMMEM = YesPlease
USE_ST_TIMESPEC = YesPlease
NEEDS_LIBICONV = YesPlease NEEDS_LIBICONV = YesPlease
BASIC_CFLAGS += -I/usr/local/include BASIC_CFLAGS += -I/usr/local/include
BASIC_LDFLAGS += -L/usr/local/lib BASIC_LDFLAGS += -L/usr/local/lib
@ -726,12 +766,14 @@ ifeq ($(uname_S),NetBSD)
BASIC_CFLAGS += -I/usr/pkg/include BASIC_CFLAGS += -I/usr/pkg/include
BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib
THREADED_DELTA_SEARCH = YesPlease THREADED_DELTA_SEARCH = YesPlease
USE_ST_TIMESPEC = YesPlease
endif endif
ifeq ($(uname_S),AIX) ifeq ($(uname_S),AIX)
NO_STRCASESTR=YesPlease NO_STRCASESTR=YesPlease
NO_MEMMEM = YesPlease NO_MEMMEM = YesPlease
NO_MKDTEMP = YesPlease NO_MKDTEMP = YesPlease
NO_STRLCPY = YesPlease NO_STRLCPY = YesPlease
NO_NSEC = YesPlease
FREAD_READS_DIRECTORIES = UnfortunatelyYes FREAD_READS_DIRECTORIES = UnfortunatelyYes
INTERNAL_QSORT = UnfortunatelyYes INTERNAL_QSORT = UnfortunatelyYes
NEEDS_LIBICONV=YesPlease NEEDS_LIBICONV=YesPlease
@ -772,9 +814,10 @@ ifeq ($(uname_S),HP-UX)
endif endif
ifneq (,$(findstring CYGWIN,$(uname_S))) ifneq (,$(findstring CYGWIN,$(uname_S)))
COMPAT_OBJS += compat/cygwin.o COMPAT_OBJS += compat/cygwin.o
UNRELIABLE_FSTAT = UnfortunatelyYes
endif endif
ifneq (,$(findstring MINGW,$(uname_S))) ifneq (,$(findstring MINGW,$(uname_S)))
NO_MMAP = YesPlease pathsep = ;
NO_PREAD = YesPlease NO_PREAD = YesPlease
NO_OPENSSL = YesPlease NO_OPENSSL = YesPlease
NO_CURL = YesPlease NO_CURL = YesPlease
@ -797,6 +840,10 @@ ifneq (,$(findstring MINGW,$(uname_S)))
RUNTIME_PREFIX = YesPlease RUNTIME_PREFIX = YesPlease
NO_POSIX_ONLY_PROGRAMS = YesPlease NO_POSIX_ONLY_PROGRAMS = YesPlease
NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
NO_NSEC = YesPlease
USE_WIN32_MMAP = YesPlease
UNRELIABLE_FSTAT = UnfortunatelyYes
OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch
COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1 COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
@ -918,6 +965,15 @@ endif
ifdef NO_ST_BLOCKS_IN_STRUCT_STAT ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT
endif 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 ifdef NO_C99_FORMAT
BASIC_CFLAGS += -DNO_C99_FORMAT BASIC_CFLAGS += -DNO_C99_FORMAT
endif endif
@ -965,6 +1021,14 @@ endif
ifdef NO_MMAP ifdef NO_MMAP
COMPAT_CFLAGS += -DNO_MMAP COMPAT_CFLAGS += -DNO_MMAP
COMPAT_OBJS += compat/mmap.o COMPAT_OBJS += compat/mmap.o
else
ifdef USE_WIN32_MMAP
COMPAT_CFLAGS += -DUSE_WIN32_MMAP
COMPAT_OBJS += compat/win32mmap.o
endif
endif
ifdef OBJECT_CREATION_USES_RENAMES
COMPAT_CFLAGS += -DOBJECT_CREATION_MODE=1
endif endif
ifdef NO_PREAD ifdef NO_PREAD
COMPAT_CFLAGS += -DNO_PREAD COMPAT_CFLAGS += -DNO_PREAD
@ -1061,11 +1125,18 @@ endif
ifdef NO_EXTERNAL_GREP ifdef NO_EXTERNAL_GREP
BASIC_CFLAGS += -DNO_EXTERNAL_GREP BASIC_CFLAGS += -DNO_EXTERNAL_GREP
endif endif
ifdef UNRELIABLE_FSTAT
BASIC_CFLAGS += -DUNRELIABLE_FSTAT
endif
ifeq ($(TCLTK_PATH),) ifeq ($(TCLTK_PATH),)
NO_TCLTK=NoThanks NO_TCLTK=NoThanks
endif endif
ifeq ($(PERL_PATH),)
NO_PERL=NoThanks
endif
QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
QUIET_SUBDIR1 = QUIET_SUBDIR1 =
@ -1140,7 +1211,9 @@ ifndef NO_TCLTK
$(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) gitexecdir='$(gitexec_instdir_SQ)' all $(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) gitexecdir='$(gitexec_instdir_SQ)' all
$(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all $(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all
endif endif
ifndef NO_PERL
$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
endif
$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1)
please_set_SHELL_PATH_to_a_more_modern_shell: please_set_SHELL_PATH_to_a_more_modern_shell:
@ -1153,6 +1226,7 @@ strip: $(PROGRAMS) git$X
git.o: git.c common-cmds.h GIT-CFLAGS git.o: git.c common-cmds.h GIT-CFLAGS
$(QUIET_CC)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \ $(QUIET_CC)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
'-DGIT_HTML_PATH="$(htmldir_SQ)"' \
$(ALL_CFLAGS) -c $(filter %.c,$^) $(ALL_CFLAGS) -c $(filter %.c,$^)
git$X: git.o $(BUILTIN_OBJS) $(GITLIBS) git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
@ -1180,13 +1254,13 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
$(QUIET_GEN)$(RM) $@ $@+ && \ $(QUIET_GEN)$(RM) $@ $@+ && \
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \ -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
-e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \ -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
$@.sh >$@+ && \ $@.sh >$@+ && \
chmod +x $@+ && \ chmod +x $@+ && \
mv $@+ $@ mv $@+ $@
ifndef NO_PERL
$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak $(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
perl/perl.mak: GIT-CFLAGS perl/Makefile perl/Makefile.PL perl/perl.mak: GIT-CFLAGS perl/Makefile perl/Makefile.PL
@ -1198,7 +1272,7 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
sed -e '1{' \ sed -e '1{' \
-e ' s|#!.*perl|#!$(PERL_PATH_SQ)|' \ -e ' s|#!.*perl|#!$(PERL_PATH_SQ)|' \
-e ' h' \ -e ' h' \
-e ' s=.*=use lib (split(/:/, $$ENV{GITPERLLIB} || "@@INSTLIBDIR@@"));=' \ -e ' s=.*=use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "@@INSTLIBDIR@@"));=' \
-e ' H' \ -e ' H' \
-e ' x' \ -e ' x' \
-e '}' \ -e '}' \
@ -1208,6 +1282,7 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
chmod +x $@+ && \ chmod +x $@+ && \
mv $@+ $@ mv $@+ $@
OTHER_PROGRAMS += gitweb/gitweb.cgi
gitweb/gitweb.cgi: gitweb/gitweb.perl gitweb/gitweb.cgi: gitweb/gitweb.perl
$(QUIET_GEN)$(RM) $@ $@+ && \ $(QUIET_GEN)$(RM) $@ $@+ && \
sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \ sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
@ -1246,6 +1321,15 @@ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
$@.sh > $@+ && \ $@.sh > $@+ && \
chmod +x $@+ && \ chmod +x $@+ && \
mv $@+ $@ mv $@+ $@
else # NO_PERL
$(patsubst %.perl,%,$(SCRIPT_PERL)) git-instaweb: % : unimplemented.sh
$(QUIET_GEN)$(RM) $@ $@+ && \
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-e 's|@@REASON@@|NO_PERL=$(NO_PERL)|g' \
unimplemented.sh >$@+ && \
chmod +x $@+ && \
mv $@+ $@
endif # NO_PERL
configure: configure.ac configure: configure.ac
$(QUIET_GEN)$(RM) $@ $<+ && \ $(QUIET_GEN)$(RM) $@ $<+ && \
@ -1361,6 +1445,8 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS
GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
@echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@ @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
@echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@ @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
@echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
@echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
### Detect Tck/Tk interpreter path changes ### Detect Tck/Tk interpreter path changes
ifndef NO_TCLTK ifndef NO_TCLTK
@ -1455,17 +1541,20 @@ install: all
$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
$(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X git-cvsserver '$(DESTDIR_SQ)$(bindir_SQ)' $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X git-cvsserver '$(DESTDIR_SQ)$(bindir_SQ)'
$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
ifndef NO_PERL
$(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
endif
ifndef NO_TCLTK ifndef NO_TCLTK
$(MAKE) -C gitk-git install $(MAKE) -C gitk-git install
$(MAKE) -C git-gui gitexecdir='$(gitexec_instdir_SQ)' install $(MAKE) -C git-gui gitexecdir='$(gitexec_instdir_SQ)' install
endif endif
ifneq (,$X) ifneq (,$X)
$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p';) $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p' -ef '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p$X' || $(RM) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p';)
endif endif
bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \ bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \ execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \
{ $(RM) "$$execdir/git-add$X" && \ { $(RM) "$$execdir/git-add$X" && \
test -z "$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
ln "$$bindir/git$X" "$$execdir/git-add$X" 2>/dev/null || \ ln "$$bindir/git$X" "$$execdir/git-add$X" 2>/dev/null || \
cp "$$bindir/git$X" "$$execdir/git-add$X"; } && \ cp "$$bindir/git$X" "$$execdir/git-add$X"; } && \
{ for p in $(filter-out git-add$X,$(BUILT_INS)); do \ { for p in $(filter-out git-add$X,$(BUILT_INS)); do \
@ -1473,7 +1562,7 @@ endif
ln "$$execdir/git-add$X" "$$execdir/$$p" 2>/dev/null || \ ln "$$execdir/git-add$X" "$$execdir/$$p" 2>/dev/null || \
ln -s "git-add$X" "$$execdir/$$p" 2>/dev/null || \ ln -s "git-add$X" "$$execdir/$$p" 2>/dev/null || \
cp "$$execdir/git-add$X" "$$execdir/$$p" || exit; \ cp "$$execdir/git-add$X" "$$execdir/$$p" || exit; \
done } && \ done; } && \
./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X" ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
install-doc: install-doc:
@ -1563,9 +1652,11 @@ clean:
$(RM) -r $(GIT_TARNAME) .doc-tmp-dir $(RM) -r $(GIT_TARNAME) .doc-tmp-dir
$(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz $(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
$(RM) $(htmldocs).tar.gz $(manpages).tar.gz $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
$(RM) gitweb/gitweb.cgi
$(MAKE) -C Documentation/ clean $(MAKE) -C Documentation/ clean
ifndef NO_PERL
$(RM) gitweb/gitweb.cgi
$(MAKE) -C perl clean $(MAKE) -C perl clean
endif
$(MAKE) -C templates/ clean $(MAKE) -C templates/ clean
$(MAKE) -C t/ clean $(MAKE) -C t/ clean
ifndef NO_TCLTK ifndef NO_TCLTK
@ -1638,3 +1729,27 @@ check-docs::
check-builtins:: check-builtins::
./check-builtins.sh ./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.5.txt Documentation/RelNotes-1.6.4.txt

10
alias.c
Просмотреть файл

@ -27,7 +27,7 @@ int split_cmdline(char *cmdline, const char ***argv)
int src, dst, count = 0, size = 16; int src, dst, count = 0, size = 16;
char quoted = 0; char quoted = 0;
*argv = xmalloc(sizeof(char*) * size); *argv = xmalloc(sizeof(char *) * size);
/* split alias_string */ /* split alias_string */
(*argv)[count++] = cmdline; (*argv)[count++] = cmdline;
@ -38,10 +38,7 @@ int split_cmdline(char *cmdline, const char ***argv)
while (cmdline[++src] while (cmdline[++src]
&& isspace(cmdline[src])) && isspace(cmdline[src]))
; /* skip */ ; /* skip */
if (count >= size) { ALLOC_GROW(*argv, count+1, size);
size += 16;
*argv = xrealloc(*argv, sizeof(char*) * size);
}
(*argv)[count++] = cmdline + dst; (*argv)[count++] = cmdline + dst;
} else if (!quoted && (c == '\'' || c == '"')) { } else if (!quoted && (c == '\'' || c == '"')) {
quoted = c; quoted = c;
@ -72,6 +69,9 @@ int split_cmdline(char *cmdline, const char ***argv)
return error("unclosed quote"); return error("unclosed quote");
} }
ALLOC_GROW(*argv, count+1, size);
(*argv)[count] = NULL;
return count; return count;
} }

Просмотреть файл

@ -57,7 +57,7 @@ DEFINE_ALLOCATOR(object, union any_object)
#define SZ_FMT "%zu" #define SZ_FMT "%zu"
#endif #endif
static void report(const char* name, unsigned int count, size_t size) static void report(const char *name, unsigned int count, size_t size)
{ {
fprintf(stderr, "%10s: %8u (" SZ_FMT " kB)\n", name, count, size); fprintf(stderr, "%10s: %8u (" SZ_FMT " kB)\n", name, count, size);
} }

Просмотреть файл

@ -180,7 +180,7 @@ static int write_tar_entry(struct archiver_args *args,
sprintf(header.mode, "%07o", mode & 07777); sprintf(header.mode, "%07o", mode & 07777);
sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0); sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0);
sprintf(header.mtime, "%011lo", args->time); sprintf(header.mtime, "%011lo", (unsigned long) args->time);
sprintf(header.uid, "%07o", 0); sprintf(header.uid, "%07o", 0);
sprintf(header.gid, "%07o", 0); sprintf(header.gid, "%07o", 0);

Просмотреть файл

@ -4,6 +4,7 @@
#include "attr.h" #include "attr.h"
#include "archive.h" #include "archive.h"
#include "parse-options.h" #include "parse-options.h"
#include "unpack-trees.h"
static char const * const archive_usage[] = { static char const * const archive_usage[] = {
"git archive [options] <tree-ish> [path...]", "git archive [options] <tree-ish> [path...]",
@ -150,6 +151,8 @@ int write_archive_entries(struct archiver_args *args,
write_archive_entry_fn_t write_entry) write_archive_entry_fn_t write_entry)
{ {
struct archiver_context context; struct archiver_context context;
struct unpack_trees_options opts;
struct tree_desc t;
int err; int err;
if (args->baselen > 0 && args->base[args->baselen - 1] == '/') { if (args->baselen > 0 && args->base[args->baselen - 1] == '/') {
@ -168,6 +171,22 @@ int write_archive_entries(struct archiver_args *args,
context.args = args; context.args = args;
context.write_entry = write_entry; context.write_entry = write_entry;
/*
* Setup index and instruct attr to read index only
*/
if (!args->worktree_attributes) {
memset(&opts, 0, sizeof(opts));
opts.index_only = 1;
opts.head_idx = -1;
opts.src_index = &the_index;
opts.dst_index = &the_index;
opts.fn = oneway_merge;
init_tree_desc(&t, args->tree->buffer, args->tree->size);
if (unpack_trees(1, &t, &opts))
return -1;
git_attr_set_direction(GIT_ATTR_INDEX, &the_index);
}
err = read_tree_recursive(args->tree, args->base, args->baselen, 0, err = read_tree_recursive(args->tree, args->base, args->baselen, 0,
args->pathspec, write_archive_entry, &context); args->pathspec, write_archive_entry, &context);
if (err == READ_TREE_RECURSIVE) if (err == READ_TREE_RECURSIVE)
@ -253,15 +272,21 @@ static int parse_archive_args(int argc, const char **argv,
const char *base = NULL; const char *base = NULL;
const char *remote = NULL; const char *remote = NULL;
const char *exec = NULL; const char *exec = NULL;
const char *output = NULL;
int compression_level = -1; int compression_level = -1;
int verbose = 0; int verbose = 0;
int i; int i;
int list = 0; int list = 0;
int worktree_attributes = 0;
struct option opts[] = { struct option opts[] = {
OPT_GROUP(""), OPT_GROUP(""),
OPT_STRING(0, "format", &format, "fmt", "archive format"), OPT_STRING(0, "format", &format, "fmt", "archive format"),
OPT_STRING(0, "prefix", &base, "prefix", OPT_STRING(0, "prefix", &base, "prefix",
"prepend prefix to each pathname in the archive"), "prepend prefix to each pathname in the archive"),
OPT_STRING(0, "output", &output, "file",
"write the archive to this file"),
OPT_BOOLEAN(0, "worktree-attributes", &worktree_attributes,
"read .gitattributes in working directory"),
OPT__VERBOSE(&verbose), OPT__VERBOSE(&verbose),
OPT__COMPR('0', &compression_level, "store only", 0), OPT__COMPR('0', &compression_level, "store only", 0),
OPT__COMPR('1', &compression_level, "compress faster", 1), OPT__COMPR('1', &compression_level, "compress faster", 1),
@ -290,6 +315,8 @@ static int parse_archive_args(int argc, const char **argv,
die("Unexpected option --remote"); die("Unexpected option --remote");
if (exec) if (exec)
die("Option --exec can only be used together with --remote"); die("Option --exec can only be used together with --remote");
if (output)
die("Unexpected option --output");
if (!base) if (!base)
base = ""; base = "";
@ -319,6 +346,7 @@ static int parse_archive_args(int argc, const char **argv,
args->verbose = verbose; args->verbose = verbose;
args->base = base; args->base = base;
args->baselen = strlen(base); args->baselen = strlen(base);
args->worktree_attributes = worktree_attributes;
return argc; return argc;
} }

Просмотреть файл

@ -10,6 +10,7 @@ struct archiver_args {
time_t time; time_t time;
const char **pathspec; const char **pathspec;
unsigned int verbose : 1; unsigned int verbose : 1;
unsigned int worktree_attributes : 1;
int compression_level; int compression_level;
}; };

87
attr.c
Просмотреть файл

@ -1,3 +1,4 @@
#define NO_THE_INDEX_COMPATIBILITY_MACROS
#include "cache.h" #include "cache.h"
#include "attr.h" #include "attr.h"
@ -223,7 +224,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
if (is_macro) if (is_macro)
res->u.attr = git_attr(name, namelen); res->u.attr = git_attr(name, namelen);
else { else {
res->u.pattern = (char*)&(res->state[num_attr]); res->u.pattern = (char *)&(res->state[num_attr]);
memcpy(res->u.pattern, name, namelen); memcpy(res->u.pattern, name, namelen);
res->u.pattern[namelen] = 0; res->u.pattern[namelen] = 0;
} }
@ -274,7 +275,7 @@ static void free_attr_elem(struct attr_stack *e)
setto == ATTR__UNKNOWN) setto == ATTR__UNKNOWN)
; ;
else else
free((char*) setto); free((char *) setto);
} }
free(a); free(a);
} }
@ -318,6 +319,9 @@ static struct attr_stack *read_attr_from_array(const char **list)
return res; 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) static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
{ {
FILE *fp = fopen(path, "r"); FILE *fp = fopen(path, "r");
@ -340,9 +344,10 @@ static void *read_index_data(const char *path)
unsigned long sz; unsigned long sz;
enum object_type type; enum object_type type;
void *data; void *data;
struct index_state *istate = use_index ? use_index : &the_index;
len = strlen(path); len = strlen(path);
pos = cache_name_pos(path, len); pos = index_name_pos(istate, path, len);
if (pos < 0) { if (pos < 0) {
/* /*
* We might be in the middle of a merge, in which * 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; int i;
for (i = -pos - 1; for (i = -pos - 1;
(pos < 0 && i < active_nr && (pos < 0 && i < istate->cache_nr &&
!strcmp(active_cache[i]->name, path)); !strcmp(istate->cache[i]->name, path));
i++) i++)
if (ce_stage(active_cache[i]) == 2) if (ce_stage(istate->cache[i]) == 2)
pos = i; pos = i;
} }
if (pos < 0) if (pos < 0)
return NULL; 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) { if (!data || type != OBJ_BLOB) {
free(data); free(data);
return NULL; return NULL;
@ -366,27 +371,17 @@ static void *read_index_data(const char *path)
return data; 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; struct attr_stack *res;
char *buf, *sp; char *buf, *sp;
int lineno = 0; 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); buf = read_index_data(path);
if (!buf) if (!buf)
return res; return NULL;
res = xcalloc(1, sizeof(*res));
for (sp = buf; *sp; ) { for (sp = buf; *sp; ) {
char *ep; char *ep;
int more; int more;
@ -401,6 +396,32 @@ static struct attr_stack *read_attr(const char *path, int macro_ok)
return res; 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 if (direction == GIT_ATTR_CHECKIN) {
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);
}
else
res = read_attr_from_index(path, macro_ok);
if (!res)
res = xcalloc(1, sizeof(*res));
return res;
}
#if DEBUG_ATTR #if DEBUG_ATTR
static void debug_info(const char *what, struct attr_stack *elem) static void debug_info(const char *what, struct attr_stack *elem)
{ {
@ -428,6 +449,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) #define debug_set(a,b,c,d) do { ; } while (0)
#endif #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) static void bootstrap_attr_stack(void)
{ {
if (!attr_stack) { if (!attr_stack) {
@ -438,7 +468,7 @@ static void bootstrap_attr_stack(void)
elem->prev = attr_stack; elem->prev = attr_stack;
attr_stack = elem; attr_stack = elem;
if (!is_bare_repository()) { if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
elem = read_attr(GITATTRIBUTES_FILE, 1); elem = read_attr(GITATTRIBUTES_FILE, 1);
elem->origin = strdup(""); elem->origin = strdup("");
elem->prev = attr_stack; elem->prev = attr_stack;
@ -505,7 +535,7 @@ static void prepare_attr_stack(const char *path, int dirlen)
/* /*
* Read from parent directories and push them down * Read from parent directories and push them down
*/ */
if (!is_bare_repository()) { if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
while (1) { while (1) {
char *cp; char *cp;
@ -642,3 +672,16 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check)
return 0; return 0;
} }
void git_attr_set_direction(enum git_attr_direction new, struct index_state *istate)
{
enum git_attr_direction old = direction;
if (is_bare_repository() && new != GIT_ATTR_INDEX)
die("BUG: non-INDEX attr direction in a bare repo");
direction = new;
if (new != old)
drop_attr_stack();
use_index = istate;
}

7
attr.h
Просмотреть файл

@ -31,4 +31,11 @@ struct git_attr_check {
int git_checkattr(const char *path, int, 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,
GIT_ATTR_INDEX,
};
void git_attr_set_direction(enum git_attr_direction, struct index_state *);
#endif /* ATTR_H */ #endif /* ATTR_H */

882
bisect.c Normal file
Просмотреть файл

@ -0,0 +1,882 @@
#include "cache.h"
#include "commit.h"
#include "diff.h"
#include "revision.h"
#include "refs.h"
#include "list-objects.h"
#include "quote.h"
#include "sha1-lookup.h"
#include "run-command.h"
#include "bisect.h"
struct sha1_array {
unsigned char (*sha1)[20];
int sha1_nr;
int sha1_alloc;
int sorted;
};
static struct sha1_array good_revs;
static struct sha1_array skipped_revs;
static const unsigned char *current_bad_sha1;
struct argv_array {
const char **argv;
int argv_nr;
int argv_alloc;
};
static const char *argv_diff_tree[] = {"diff-tree", "--pretty", NULL, NULL};
static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
/* bits #0-15 in revision.h */
#define COUNTED (1u<<16)
/*
* This is a truly stupid algorithm, but it's only
* used for bisection, and we just don't care enough.
*
* We care just barely enough to avoid recursing for
* non-merge entries.
*/
static int count_distance(struct commit_list *entry)
{
int nr = 0;
while (entry) {
struct commit *commit = entry->item;
struct commit_list *p;
if (commit->object.flags & (UNINTERESTING | COUNTED))
break;
if (!(commit->object.flags & TREESAME))
nr++;
commit->object.flags |= COUNTED;
p = commit->parents;
entry = p;
if (p) {
p = p->next;
while (p) {
nr += count_distance(p);
p = p->next;
}
}
}
return nr;
}
static void clear_distance(struct commit_list *list)
{
while (list) {
struct commit *commit = list->item;
commit->object.flags &= ~COUNTED;
list = list->next;
}
}
#define DEBUG_BISECT 0
static inline int weight(struct commit_list *elem)
{
return *((int*)(elem->item->util));
}
static inline void weight_set(struct commit_list *elem, int weight)
{
*((int*)(elem->item->util)) = weight;
}
static int count_interesting_parents(struct commit *commit)
{
struct commit_list *p;
int count;
for (count = 0, p = commit->parents; p; p = p->next) {
if (p->item->object.flags & UNINTERESTING)
continue;
count++;
}
return count;
}
static inline int halfway(struct commit_list *p, int nr)
{
/*
* Don't short-cut something we are not going to return!
*/
if (p->item->object.flags & TREESAME)
return 0;
if (DEBUG_BISECT)
return 0;
/*
* 2 and 3 are halfway of 5.
* 3 is halfway of 6 but 2 and 4 are not.
*/
switch (2 * weight(p) - nr) {
case -1: case 0: case 1:
return 1;
default:
return 0;
}
}
#if !DEBUG_BISECT
#define show_list(a,b,c,d) do { ; } while (0)
#else
static void show_list(const char *debug, int counted, int nr,
struct commit_list *list)
{
struct commit_list *p;
fprintf(stderr, "%s (%d/%d)\n", debug, counted, nr);
for (p = list; p; p = p->next) {
struct commit_list *pp;
struct commit *commit = p->item;
unsigned flags = commit->object.flags;
enum object_type type;
unsigned long size;
char *buf = read_sha1_file(commit->object.sha1, &type, &size);
char *ep, *sp;
fprintf(stderr, "%c%c%c ",
(flags & TREESAME) ? ' ' : 'T',
(flags & UNINTERESTING) ? 'U' : ' ',
(flags & COUNTED) ? 'C' : ' ');
if (commit->util)
fprintf(stderr, "%3d", weight(p));
else
fprintf(stderr, "---");
fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.sha1));
for (pp = commit->parents; pp; pp = pp->next)
fprintf(stderr, " %.*s", 8,
sha1_to_hex(pp->item->object.sha1));
sp = strstr(buf, "\n\n");
if (sp) {
sp += 2;
for (ep = sp; *ep && *ep != '\n'; ep++)
;
fprintf(stderr, " %.*s", (int)(ep - sp), sp);
}
fprintf(stderr, "\n");
}
}
#endif /* DEBUG_BISECT */
static struct commit_list *best_bisection(struct commit_list *list, int nr)
{
struct commit_list *p, *best;
int best_distance = -1;
best = list;
for (p = list; p; p = p->next) {
int distance;
unsigned flags = p->item->object.flags;
if (flags & TREESAME)
continue;
distance = weight(p);
if (nr - distance < distance)
distance = nr - distance;
if (distance > best_distance) {
best = p;
best_distance = distance;
}
}
return best;
}
struct commit_dist {
struct commit *commit;
int distance;
};
static int compare_commit_dist(const void *a_, const void *b_)
{
struct commit_dist *a, *b;
a = (struct commit_dist *)a_;
b = (struct commit_dist *)b_;
if (a->distance != b->distance)
return b->distance - a->distance; /* desc sort */
return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
}
static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
{
struct commit_list *p;
struct commit_dist *array = xcalloc(nr, sizeof(*array));
int cnt, i;
for (p = list, cnt = 0; p; p = p->next) {
int distance;
unsigned flags = p->item->object.flags;
if (flags & TREESAME)
continue;
distance = weight(p);
if (nr - distance < distance)
distance = nr - distance;
array[cnt].commit = p->item;
array[cnt].distance = distance;
cnt++;
}
qsort(array, cnt, sizeof(*array), compare_commit_dist);
for (p = list, i = 0; i < cnt; i++) {
struct name_decoration *r = xmalloc(sizeof(*r) + 100);
struct object *obj = &(array[i].commit->object);
sprintf(r->name, "dist=%d", array[i].distance);
r->next = add_decoration(&name_decoration, obj, r);
p->item = array[i].commit;
p = p->next;
}
if (p)
p->next = NULL;
free(array);
return list;
}
/*
* zero or positive weight is the number of interesting commits it can
* reach, including itself. Especially, weight = 0 means it does not
* reach any tree-changing commits (e.g. just above uninteresting one
* but traversal is with pathspec).
*
* weight = -1 means it has one parent and its distance is yet to
* be computed.
*
* weight = -2 means it has more than one parent and its distance is
* unknown. After running count_distance() first, they will get zero
* or positive distance.
*/
static struct commit_list *do_find_bisection(struct commit_list *list,
int nr, int *weights,
int find_all)
{
int n, counted;
struct commit_list *p;
counted = 0;
for (n = 0, p = list; p; p = p->next) {
struct commit *commit = p->item;
unsigned flags = commit->object.flags;
p->item->util = &weights[n++];
switch (count_interesting_parents(commit)) {
case 0:
if (!(flags & TREESAME)) {
weight_set(p, 1);
counted++;
show_list("bisection 2 count one",
counted, nr, list);
}
/*
* otherwise, it is known not to reach any
* tree-changing commit and gets weight 0.
*/
break;
case 1:
weight_set(p, -1);
break;
default:
weight_set(p, -2);
break;
}
}
show_list("bisection 2 initialize", counted, nr, list);
/*
* If you have only one parent in the resulting set
* then you can reach one commit more than that parent
* can reach. So we do not have to run the expensive
* count_distance() for single strand of pearls.
*
* However, if you have more than one parents, you cannot
* just add their distance and one for yourself, since
* they usually reach the same ancestor and you would
* end up counting them twice that way.
*
* So we will first count distance of merges the usual
* way, and then fill the blanks using cheaper algorithm.
*/
for (p = list; p; p = p->next) {
if (p->item->object.flags & UNINTERESTING)
continue;
if (weight(p) != -2)
continue;
weight_set(p, count_distance(p));
clear_distance(list);
/* Does it happen to be at exactly half-way? */
if (!find_all && halfway(p, nr))
return p;
counted++;
}
show_list("bisection 2 count_distance", counted, nr, list);
while (counted < nr) {
for (p = list; p; p = p->next) {
struct commit_list *q;
unsigned flags = p->item->object.flags;
if (0 <= weight(p))
continue;
for (q = p->item->parents; q; q = q->next) {
if (q->item->object.flags & UNINTERESTING)
continue;
if (0 <= weight(q))
break;
}
if (!q)
continue;
/*
* weight for p is unknown but q is known.
* add one for p itself if p is to be counted,
* otherwise inherit it from q directly.
*/
if (!(flags & TREESAME)) {
weight_set(p, weight(q)+1);
counted++;
show_list("bisection 2 count one",
counted, nr, list);
}
else
weight_set(p, weight(q));
/* Does it happen to be at exactly half-way? */
if (!find_all && halfway(p, nr))
return p;
}
}
show_list("bisection 2 counted all", counted, nr, list);
if (!find_all)
return best_bisection(list, nr);
else
return best_bisection_sorted(list, nr);
}
struct commit_list *find_bisection(struct commit_list *list,
int *reaches, int *all,
int find_all)
{
int nr, on_list;
struct commit_list *p, *best, *next, *last;
int *weights;
show_list("bisection 2 entry", 0, 0, list);
/*
* Count the number of total and tree-changing items on the
* list, while reversing the list.
*/
for (nr = on_list = 0, last = NULL, p = list;
p;
p = next) {
unsigned flags = p->item->object.flags;
next = p->next;
if (flags & UNINTERESTING)
continue;
p->next = last;
last = p;
if (!(flags & TREESAME))
nr++;
on_list++;
}
list = last;
show_list("bisection 2 sorted", 0, nr, list);
*all = nr;
weights = xcalloc(on_list, sizeof(*weights));
/* Do the real work of finding bisection commit. */
best = do_find_bisection(list, nr, weights, find_all);
if (best) {
if (!find_all)
best->next = NULL;
*reaches = weight(best);
}
free(weights);
return best;
}
static void argv_array_push(struct argv_array *array, const char *string)
{
ALLOC_GROW(array->argv, array->argv_nr + 1, array->argv_alloc);
array->argv[array->argv_nr++] = string;
}
static void argv_array_push_sha1(struct argv_array *array,
const unsigned char *sha1,
const char *format)
{
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, format, sha1_to_hex(sha1));
argv_array_push(array, strbuf_detach(&buf, NULL));
}
static void sha1_array_push(struct sha1_array *array,
const unsigned char *sha1)
{
ALLOC_GROW(array->sha1, array->sha1_nr + 1, array->sha1_alloc);
hashcpy(array->sha1[array->sha1_nr++], sha1);
}
static int register_ref(const char *refname, const unsigned char *sha1,
int flags, void *cb_data)
{
if (!strcmp(refname, "bad")) {
current_bad_sha1 = sha1;
} else if (!prefixcmp(refname, "good-")) {
sha1_array_push(&good_revs, sha1);
} else if (!prefixcmp(refname, "skip-")) {
sha1_array_push(&skipped_revs, sha1);
}
return 0;
}
static int read_bisect_refs(void)
{
return for_each_ref_in("refs/bisect/", register_ref, NULL);
}
void read_bisect_paths(struct argv_array *array)
{
struct strbuf str = STRBUF_INIT;
const char *filename = git_path("BISECT_NAMES");
FILE *fp = fopen(filename, "r");
if (!fp)
die("Could not open file '%s': %s", filename, strerror(errno));
while (strbuf_getline(&str, fp, '\n') != EOF) {
char *quoted;
int res;
strbuf_trim(&str);
quoted = strbuf_detach(&str, NULL);
res = sq_dequote_to_argv(quoted, &array->argv,
&array->argv_nr, &array->argv_alloc);
if (res)
die("Badly quoted content in file '%s': %s",
filename, quoted);
}
strbuf_release(&str);
fclose(fp);
}
static int array_cmp(const void *a, const void *b)
{
return hashcmp(a, b);
}
static void sort_sha1_array(struct sha1_array *array)
{
qsort(array->sha1, array->sha1_nr, sizeof(*array->sha1), array_cmp);
array->sorted = 1;
}
static const unsigned char *sha1_access(size_t index, void *table)
{
unsigned char (*array)[20] = table;
return array[index];
}
static int lookup_sha1_array(struct sha1_array *array,
const unsigned char *sha1)
{
if (!array->sorted)
sort_sha1_array(array);
return sha1_pos(sha1, array->sha1, array->sha1_nr, sha1_access);
}
static char *join_sha1_array_hex(struct sha1_array *array, char delim)
{
struct strbuf joined_hexs = STRBUF_INIT;
int i;
for (i = 0; i < array->sha1_nr; i++) {
strbuf_addstr(&joined_hexs, sha1_to_hex(array->sha1[i]));
if (i + 1 < array->sha1_nr)
strbuf_addch(&joined_hexs, delim);
}
return strbuf_detach(&joined_hexs, NULL);
}
struct commit_list *filter_skipped(struct commit_list *list,
struct commit_list **tried,
int show_all)
{
struct commit_list *filtered = NULL, **f = &filtered;
*tried = NULL;
if (!skipped_revs.sha1_nr)
return list;
while (list) {
struct commit_list *next = list->next;
list->next = NULL;
if (0 <= lookup_sha1_array(&skipped_revs,
list->item->object.sha1)) {
/* Move current to tried list */
*tried = list;
tried = &list->next;
} else {
if (!show_all)
return list;
/* Move current to filtered list */
*f = list;
f = &list->next;
}
list = next;
}
return filtered;
}
static void bisect_rev_setup(struct rev_info *revs, const char *prefix)
{
struct argv_array rev_argv = { NULL, 0, 0 };
int i;
init_revisions(revs, prefix);
revs->abbrev = 0;
revs->commit_format = CMIT_FMT_UNSPECIFIED;
/* rev_argv.argv[0] will be ignored by setup_revisions */
argv_array_push(&rev_argv, xstrdup("bisect_rev_setup"));
argv_array_push_sha1(&rev_argv, current_bad_sha1, "%s");
for (i = 0; i < good_revs.sha1_nr; i++)
argv_array_push_sha1(&rev_argv, good_revs.sha1[i], "^%s");
argv_array_push(&rev_argv, xstrdup("--"));
read_bisect_paths(&rev_argv);
argv_array_push(&rev_argv, NULL);
setup_revisions(rev_argv.argv_nr, rev_argv.argv, revs, NULL);
revs->limited = 1;
}
static void bisect_common(struct rev_info *revs, int *reaches, int *all)
{
if (prepare_revision_walk(revs))
die("revision walk setup failed");
if (revs->tree_objects)
mark_edges_uninteresting(revs->commits, revs, NULL);
revs->commits = find_bisection(revs->commits, reaches, all,
!!skipped_revs.sha1_nr);
}
static void exit_if_skipped_commits(struct commit_list *tried,
const unsigned char *bad)
{
if (!tried)
return;
printf("There are only 'skip'ped commits left to test.\n"
"The first bad commit could be any of:\n");
print_commit_list(tried, "%s\n", "%s\n");
if (bad)
printf("%s\n", sha1_to_hex(bad));
printf("We cannot bisect more!\n");
exit(2);
}
static int is_expected_rev(const unsigned char *sha1)
{
const char *filename = git_path("BISECT_EXPECTED_REV");
struct stat st;
struct strbuf str = STRBUF_INIT;
FILE *fp;
int res = 0;
if (stat(filename, &st) || !S_ISREG(st.st_mode))
return 0;
fp = fopen(filename, "r");
if (!fp)
return 0;
if (strbuf_getline(&str, fp, '\n') != EOF)
res = !strcmp(str.buf, sha1_to_hex(sha1));
strbuf_release(&str);
fclose(fp);
return res;
}
static void mark_expected_rev(char *bisect_rev_hex)
{
int len = strlen(bisect_rev_hex);
const char *filename = git_path("BISECT_EXPECTED_REV");
int fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (fd < 0)
die("could not create file '%s': %s",
filename, strerror(errno));
bisect_rev_hex[len] = '\n';
write_or_die(fd, bisect_rev_hex, len + 1);
bisect_rev_hex[len] = '\0';
if (close(fd) < 0)
die("closing file %s: %s", filename, strerror(errno));
}
static int bisect_checkout(char *bisect_rev_hex)
{
int res;
mark_expected_rev(bisect_rev_hex);
argv_checkout[2] = bisect_rev_hex;
res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
if (res)
exit(res);
argv_show_branch[1] = bisect_rev_hex;
return run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
}
static struct commit *get_commit_reference(const unsigned char *sha1)
{
struct commit *r = lookup_commit_reference(sha1);
if (!r)
die("Not a valid commit name %s", sha1_to_hex(sha1));
return r;
}
static struct commit **get_bad_and_good_commits(int *rev_nr)
{
int len = 1 + good_revs.sha1_nr;
struct commit **rev = xmalloc(len * sizeof(*rev));
int i, n = 0;
rev[n++] = get_commit_reference(current_bad_sha1);
for (i = 0; i < good_revs.sha1_nr; i++)
rev[n++] = get_commit_reference(good_revs.sha1[i]);
*rev_nr = n;
return rev;
}
static void handle_bad_merge_base(void)
{
if (is_expected_rev(current_bad_sha1)) {
char *bad_hex = sha1_to_hex(current_bad_sha1);
char *good_hex = join_sha1_array_hex(&good_revs, ' ');
fprintf(stderr, "The merge base %s is bad.\n"
"This means the bug has been fixed "
"between %s and [%s].\n",
bad_hex, bad_hex, good_hex);
exit(3);
}
fprintf(stderr, "Some good revs are not ancestor of the bad rev.\n"
"git bisect cannot work properly in this case.\n"
"Maybe you mistake good and bad revs?\n");
exit(1);
}
void handle_skipped_merge_base(const unsigned char *mb)
{
char *mb_hex = sha1_to_hex(mb);
char *bad_hex = sha1_to_hex(current_bad_sha1);
char *good_hex = join_sha1_array_hex(&good_revs, ' ');
fprintf(stderr, "Warning: the merge base between %s and [%s] "
"must be skipped.\n"
"So we cannot be sure the first bad commit is "
"between %s and %s.\n"
"We continue anyway.\n",
bad_hex, good_hex, mb_hex, bad_hex);
free(good_hex);
}
/*
* "check_merge_bases" checks that merge bases are not "bad".
*
* - If one is "bad", it means the user assumed something wrong
* and we must exit with a non 0 error code.
* - If one is "good", that's good, we have nothing to do.
* - If one is "skipped", we can't know but we should warn.
* - If we don't know, we should check it out and ask the user to test.
*/
static void check_merge_bases(void)
{
struct commit_list *result;
int rev_nr;
struct commit **rev = get_bad_and_good_commits(&rev_nr);
result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0);
for (; result; result = result->next) {
const unsigned char *mb = result->item->object.sha1;
if (!hashcmp(mb, current_bad_sha1)) {
handle_bad_merge_base();
} else if (0 <= lookup_sha1_array(&good_revs, mb)) {
continue;
} else if (0 <= lookup_sha1_array(&skipped_revs, mb)) {
handle_skipped_merge_base(mb);
} else {
printf("Bisecting: a merge base must be tested\n");
exit(bisect_checkout(sha1_to_hex(mb)));
}
}
free(rev);
free_commit_list(result);
}
/*
* This function runs the command "git rev-list $_good ^$_bad"
* and returns 1 if it produces some output, 0 otherwise.
*/
static int check_ancestors(void)
{
struct argv_array rev_argv = { NULL, 0, 0 };
struct strbuf str = STRBUF_INIT;
int i, result = 0;
struct child_process rls;
FILE *rls_fout;
argv_array_push(&rev_argv, xstrdup("rev-list"));
argv_array_push_sha1(&rev_argv, current_bad_sha1, "^%s");
for (i = 0; i < good_revs.sha1_nr; i++)
argv_array_push_sha1(&rev_argv, good_revs.sha1[i], "%s");
argv_array_push(&rev_argv, NULL);
memset(&rls, 0, sizeof(rls));
rls.argv = rev_argv.argv;
rls.out = -1;
rls.git_cmd = 1;
if (start_command(&rls))
die("Could not launch 'git rev-list' command.");
rls_fout = fdopen(rls.out, "r");
while (strbuf_getline(&str, rls_fout, '\n') != EOF) {
strbuf_trim(&str);
if (*str.buf) {
result = 1;
break;
}
}
fclose(rls_fout);
finish_command(&rls);
return result;
}
/*
* "check_good_are_ancestors_of_bad" checks that all "good" revs are
* ancestor of the "bad" rev.
*
* If that's not the case, we need to check the merge bases.
* If a merge base must be tested by the user, its source code will be
* checked out to be tested by the user and we will exit.
*/
static void check_good_are_ancestors_of_bad(const char *prefix)
{
const char *filename = git_path("BISECT_ANCESTORS_OK");
struct stat st;
int fd;
if (!current_bad_sha1)
die("a bad revision is needed");
/* Check if file BISECT_ANCESTORS_OK exists. */
if (!stat(filename, &st) && S_ISREG(st.st_mode))
return;
/* Bisecting with no good rev is ok. */
if (good_revs.sha1_nr == 0)
return;
if (check_ancestors())
check_merge_bases();
/* Create file BISECT_ANCESTORS_OK. */
fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (fd < 0)
warning("could not create file '%s': %s",
filename, strerror(errno));
else
close(fd);
}
/*
* We use the convention that exiting with an exit code 10 means that
* the bisection process finished successfully.
* In this case the calling shell script should exit 0.
*/
int bisect_next_all(const char *prefix)
{
struct rev_info revs;
struct commit_list *tried;
int reaches = 0, all = 0, nr;
const unsigned char *bisect_rev;
char bisect_rev_hex[41];
if (read_bisect_refs())
die("reading bisect refs failed");
check_good_are_ancestors_of_bad(prefix);
bisect_rev_setup(&revs, prefix);
bisect_common(&revs, &reaches, &all);
revs.commits = filter_skipped(revs.commits, &tried, 0);
if (!revs.commits) {
/*
* We should exit here only if the "bad"
* commit is also a "skip" commit.
*/
exit_if_skipped_commits(tried, NULL);
printf("%s was both good and bad\n",
sha1_to_hex(current_bad_sha1));
exit(1);
}
bisect_rev = revs.commits->item->object.sha1;
memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), 41);
if (!hashcmp(bisect_rev, current_bad_sha1)) {
exit_if_skipped_commits(tried, current_bad_sha1);
printf("%s is first bad commit\n", bisect_rev_hex);
argv_diff_tree[2] = bisect_rev_hex;
run_command_v_opt(argv_diff_tree, RUN_GIT_CMD);
/* This means the bisection process succeeded. */
exit(10);
}
nr = all - reaches - 1;
printf("Bisecting: %d revisions left to test after this "
"(roughly %d steps)\n", nr, estimate_bisect_steps(all));
return bisect_checkout(bisect_rev_hex);
}

34
bisect.h Normal file
Просмотреть файл

@ -0,0 +1,34 @@
#ifndef BISECT_H
#define BISECT_H
extern struct commit_list *find_bisection(struct commit_list *list,
int *reaches, int *all,
int find_all);
extern struct commit_list *filter_skipped(struct commit_list *list,
struct commit_list **tried,
int show_all);
extern void print_commit_list(struct commit_list *list,
const char *format_cur,
const char *format_last);
/* bisect_show_flags flags in struct rev_list_info */
#define BISECT_SHOW_ALL (1<<0)
#define BISECT_SHOW_TRIED (1<<1)
struct rev_list_info {
struct rev_info *revs;
int bisect_show_flags;
int show_timestamp;
int hdr_termination;
const char *header_prefix;
};
extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all);
extern int bisect_next_all(const char *prefix);
extern int estimate_bisect_steps(int all);
#endif

Просмотреть файл

@ -32,21 +32,59 @@ static int find_tracked_branch(struct remote *remote, void *priv)
return 0; return 0;
} }
static int should_setup_rebase(const struct tracking *tracking) static int should_setup_rebase(const char *origin)
{ {
switch (autorebase) { switch (autorebase) {
case AUTOREBASE_NEVER: case AUTOREBASE_NEVER:
return 0; return 0;
case AUTOREBASE_LOCAL: case AUTOREBASE_LOCAL:
return tracking->remote == NULL; return origin == NULL;
case AUTOREBASE_REMOTE: case AUTOREBASE_REMOTE:
return tracking->remote != NULL; return origin != NULL;
case AUTOREBASE_ALWAYS: case AUTOREBASE_ALWAYS:
return 1; return 1;
} }
return 0; 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 * 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 * 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, static int setup_tracking(const char *new_ref, const char *orig_ref,
enum branch_track track) enum branch_track track)
{ {
char key[1024];
struct tracking tracking; struct tracking tracking;
if (strlen(new_ref) > 1024 - 7 - 7 - 1) 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", return error("Not tracking: ambiguous information for ref %s",
orig_ref); orig_ref);
sprintf(key, "branch.%s.remote", new_ref); install_branch_config(BRANCH_CONFIG_VERBOSE, new_ref, tracking.remote,
git_config_set(key, tracking.remote ? tracking.remote : "."); tracking.src ? tracking.src : orig_ref);
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);
free(tracking.src);
return 0; return 0;
} }
@ -106,16 +134,8 @@ void create_branch(const char *head,
char *real_ref, msg[PATH_MAX + 20]; char *real_ref, msg[PATH_MAX + 20];
struct strbuf ref = STRBUF_INIT; struct strbuf ref = STRBUF_INIT;
int forcing = 0; int forcing = 0;
int len;
len = strlen(name); if (strbuf_check_branch_ref(&ref, name))
if (interpret_nth_last_branch(name, &ref) != len) {
strbuf_reset(&ref);
strbuf_add(&ref, name, len);
}
strbuf_splice(&ref, 0, 0, "refs/heads/", 11);
if (check_ref_format(ref.buf))
die("'%s' is not a valid branch name.", name); die("'%s' is not a valid branch name.", name);
if (resolve_ref(ref.buf, sha1, 1, NULL)) { if (resolve_ref(ref.buf, sha1, 1, NULL)) {

Просмотреть файл

@ -21,4 +21,11 @@ void create_branch(const char *head, const char *name, const char *start_name,
*/ */
void remove_branch_state(void); 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 #endif

Просмотреть файл

@ -10,12 +10,14 @@
#include "cache-tree.h" #include "cache-tree.h"
#include "run-command.h" #include "run-command.h"
#include "parse-options.h" #include "parse-options.h"
#include "diff.h"
#include "revision.h"
static const char * const builtin_add_usage[] = { static const char * const builtin_add_usage[] = {
"git add [options] [--] <filepattern>...", "git add [options] [--] <filepattern>...",
NULL NULL
}; };
static int patch_interactive, add_interactive; static int patch_interactive, add_interactive, edit_interactive;
static int take_worktree_changes; static int take_worktree_changes;
static void fill_pathspec_matches(const char **pathspec, char *seen, int specs) static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)
@ -61,7 +63,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
fill_pathspec_matches(pathspec, seen, specs); fill_pathspec_matches(pathspec, seen, specs);
for (i = 0; i < specs; i++) { for (i = 0; i < specs; i++) {
if (!seen[i] && !file_exists(pathspec[i])) if (!seen[i] && pathspec[i][0] && !file_exists(pathspec[i]))
die("pathspec '%s' did not match any files", die("pathspec '%s' did not match any files",
pathspec[i]); pathspec[i]);
} }
@ -104,7 +106,7 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec,
/* Set up the default git porcelain excludes */ /* Set up the default git porcelain excludes */
memset(dir, 0, sizeof(*dir)); memset(dir, 0, sizeof(*dir));
if (!ignored_too) { if (!ignored_too) {
dir->collect_ignored = 1; dir->flags |= DIR_COLLECT_IGNORED;
setup_standard_excludes(dir); setup_standard_excludes(dir);
} }
@ -148,7 +150,7 @@ static const char **validate_pathspec(int argc, const char **argv, const char *p
if (pathspec) { if (pathspec) {
const char **p; const char **p;
for (p = pathspec; *p; 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; int len = prefix ? strlen(prefix) : 0;
die("'%s' is beyond a symbolic link", *p + len); die("'%s' is beyond a symbolic link", *p + len);
} }
@ -187,6 +189,51 @@ int interactive_add(int argc, const char **argv, const char *prefix)
return status; return status;
} }
int edit_patch(int argc, const char **argv, const char *prefix)
{
char *file = xstrdup(git_path("ADD_EDIT.patch"));
const char *apply_argv[] = { "apply", "--recount", "--cached",
file, NULL };
struct child_process child;
struct rev_info rev;
int out;
struct stat st;
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
if (read_cache() < 0)
die ("Could not read the index");
init_revisions(&rev, prefix);
rev.diffopt.context = 7;
argc = setup_revisions(argc, argv, &rev, NULL);
rev.diffopt.output_format = DIFF_FORMAT_PATCH;
out = open(file, O_CREAT | O_WRONLY, 0644);
if (out < 0)
die ("Could not open '%s' for writing.", file);
rev.diffopt.file = fdopen(out, "w");
rev.diffopt.close_file = 1;
if (run_diff_files(&rev, 0))
die ("Could not write patch");
launch_editor(file, NULL, NULL);
if (stat(file, &st))
die("Could not stat '%s'", file);
if (!st.st_size)
die("Empty patch. Aborted.");
memset(&child, 0, sizeof(child));
child.git_cmd = 1;
child.argv = apply_argv;
if (run_command(&child))
die ("Could not apply '%s'", file);
unlink(file);
return 0;
}
static struct lock_file lock_file; static struct lock_file lock_file;
static const char ignore_error[] = static const char ignore_error[] =
@ -201,6 +248,7 @@ static struct option builtin_add_options[] = {
OPT_GROUP(""), OPT_GROUP(""),
OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"), OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"),
OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"), OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"),
OPT_BOOLEAN('e', "edit", &edit_interactive, "edit current diff and apply"),
OPT_BOOLEAN('f', "force", &ignored_too, "allow adding otherwise ignored files"), OPT_BOOLEAN('f', "force", &ignored_too, "allow adding otherwise ignored files"),
OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"), OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"), OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"),
@ -251,14 +299,19 @@ int cmd_add(int argc, const char **argv, const char *prefix)
int require_pathspec; int require_pathspec;
argc = parse_options(argc, argv, builtin_add_options, argc = parse_options(argc, argv, builtin_add_options,
builtin_add_usage, 0); builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
if (patch_interactive) if (patch_interactive)
add_interactive = 1; add_interactive = 1;
if (add_interactive) if (add_interactive)
exit(interactive_add(argc, argv, prefix)); exit(interactive_add(argc - 1, argv + 1, prefix));
git_config(add_config, NULL); git_config(add_config, NULL);
if (edit_interactive)
return(edit_patch(argc, argv, prefix));
argc--;
argv++;
if (addremove && take_worktree_changes) if (addremove && take_worktree_changes)
die("-A and -u are mutually incompatible"); die("-A and -u are mutually incompatible");
if ((addremove || take_worktree_changes) && !argc) { if ((addremove || take_worktree_changes) && !argc) {

Просмотреть файл

@ -2394,7 +2394,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 * In such a case, path "new_name" does not exist as
* far as git is concerned. * 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 0;
return error("%s: already exists in working directory", new_name); return error("%s: already exists in working directory", new_name);
@ -2487,7 +2487,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
if ((st_mode ^ patch->old_mode) & S_IFMT) if ((st_mode ^ patch->old_mode) & S_IFMT)
return error("%s: wrong type", old_name); return error("%s: wrong type", old_name);
if (st_mode != patch->old_mode) 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); old_name, st_mode, patch->old_mode);
if (!patch->new_mode && !patch->is_delete) if (!patch->new_mode && !patch->is_delete)
patch->new_mode = st_mode; patch->new_mode = st_mode;
@ -2781,7 +2781,7 @@ static void remove_file(struct patch *patch, int rmdir_empty)
if (rmdir(patch->old_name)) if (rmdir(patch->old_name))
warning("unable to remove submodule %s", warning("unable to remove submodule %s",
patch->old_name); patch->old_name);
} else if (!unlink(patch->old_name) && rmdir_empty) { } else if (!unlink_or_warn(patch->old_name) && rmdir_empty) {
remove_path(patch->old_name); remove_path(patch->old_name);
} }
} }
@ -2891,7 +2891,7 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
if (!try_create_file(newpath, mode, buf, size)) { if (!try_create_file(newpath, mode, buf, size)) {
if (!rename(newpath, path)) if (!rename(newpath, path))
return; return;
unlink(newpath); unlink_or_warn(newpath);
break; break;
} }
if (errno != EEXIST) if (errno != EEXIST)
@ -2971,8 +2971,7 @@ static int write_out_one_reject(struct patch *patch)
cnt = strlen(patch->new_name); cnt = strlen(patch->new_name);
if (ARRAY_SIZE(namebuf) <= cnt + 5) { if (ARRAY_SIZE(namebuf) <= cnt + 5) {
cnt = ARRAY_SIZE(namebuf) - 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); cnt - 1, patch->new_name);
} }
memcpy(namebuf, patch->new_name, cnt); memcpy(namebuf, patch->new_name, cnt);
@ -3263,10 +3262,10 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
"ignore additions made by the patch"), "ignore additions made by the patch"),
OPT_BOOLEAN(0, "stat", &diffstat, OPT_BOOLEAN(0, "stat", &diffstat,
"instead of applying the patch, output diffstat for the input"), "instead of applying the patch, output diffstat for the input"),
OPT_BOOLEAN(0, "allow-binary-replacement", &binary, { OPTION_BOOLEAN, 0, "allow-binary-replacement", &binary,
"now no-op"), NULL, "old option, now no-op", PARSE_OPT_HIDDEN },
OPT_BOOLEAN(0, "binary", &binary, { OPTION_BOOLEAN, 0, "binary", &binary,
"now no-op"), NULL, "old option, now no-op", PARSE_OPT_HIDDEN },
OPT_BOOLEAN(0, "numstat", &numstat, OPT_BOOLEAN(0, "numstat", &numstat,
"shows number of added and deleted lines in decimal notation"), "shows number of added and deleted lines in decimal notation"),
OPT_BOOLEAN(0, "summary", &summary, OPT_BOOLEAN(0, "summary", &summary,
@ -3358,8 +3357,8 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
squelch_whitespace_errors < whitespace_error) { squelch_whitespace_errors < whitespace_error) {
int squelched = int squelched =
whitespace_error - squelch_whitespace_errors; whitespace_error - squelch_whitespace_errors;
fprintf(stderr, "warning: squelched %d " warning("squelched %d "
"whitespace error%s\n", "whitespace error%s",
squelched, squelched,
squelched == 1 ? "" : "s"); squelched == 1 ? "" : "s");
} }
@ -3369,12 +3368,12 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
whitespace_error == 1 ? "" : "s", whitespace_error == 1 ? "" : "s",
whitespace_error == 1 ? "s" : ""); whitespace_error == 1 ? "s" : "");
if (applied_after_fixing_ws && apply) if (applied_after_fixing_ws && apply)
fprintf(stderr, "warning: %d line%s applied after" warning("%d line%s applied after"
" fixing whitespace errors.\n", " fixing whitespace errors.",
applied_after_fixing_ws, applied_after_fixing_ws,
applied_after_fixing_ws == 1 ? "" : "s"); applied_after_fixing_ws == 1 ? "" : "s");
else if (whitespace_error) 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,
whitespace_error == 1 ? "" : "s", whitespace_error == 1 ? "" : "s",
whitespace_error == 1 ? "s" : ""); whitespace_error == 1 ? "s" : "");

Просмотреть файл

@ -5,44 +5,35 @@
#include "cache.h" #include "cache.h"
#include "builtin.h" #include "builtin.h"
#include "archive.h" #include "archive.h"
#include "parse-options.h"
#include "pkt-line.h" #include "pkt-line.h"
#include "sideband.h" #include "sideband.h"
static int run_remote_archiver(const char *remote, int argc, static void create_output_file(const char *output_file)
const char **argv) {
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]; char *url, buf[LARGE_PACKET_MAX];
int fd[2], i, len, rv; int fd[2], i, len, rv;
struct child_process *conn; 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); url = xstrdup(remote);
conn = git_connect(fd, url, exec, 0); conn = git_connect(fd, url, exec, 0);
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++)
if (i == exec_at || i == exec_value_at)
continue;
packet_write(fd[1], "argument %s\n", argv[i]); packet_write(fd[1], "argument %s\n", argv[i]);
}
packet_flush(fd[1]); packet_flush(fd[1]);
len = packet_read_line(fd[0], buf, sizeof(buf)); 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"); die("git archive: expected a flush");
/* Now, start reading from fd[0] and spit it out to stdout */ /* 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[0]);
close(fd[1]); close(fd[1]);
rv |= finish_connect(conn); rv |= finish_connect(conn);
@ -69,51 +60,33 @@ static int run_remote_archiver(const char *remote, int argc,
return !!rv; return !!rv;
} }
static const char *extract_remote_arg(int *ac, const char **av) #define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH | \
{ PARSE_OPT_KEEP_ARGV0 | \
int ix, iy, cnt = *ac; PARSE_OPT_KEEP_UNKNOWN | \
int no_more_options = 0; PARSE_OPT_NO_INTERNAL_HELP )
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;
}
int cmd_archive(int argc, const char **argv, const char *prefix) 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; 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) if (remote)
return run_remote_archiver(remote, argc, argv); return run_remote_archiver(argc, argv, remote, exec);
setvbuf(stderr, NULL, _IOLBF, BUFSIZ); setvbuf(stderr, NULL, _IOLBF, BUFSIZ);

27
builtin-bisect--helper.c Normal file
Просмотреть файл

@ -0,0 +1,27 @@
#include "builtin.h"
#include "cache.h"
#include "parse-options.h"
#include "bisect.h"
static const char * const git_bisect_helper_usage[] = {
"git bisect--helper --next-all",
NULL
};
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
{
int next_all = 0;
struct option options[] = {
OPT_BOOLEAN(0, "next-all", &next_all,
"perform 'git bisect next'"),
OPT_END()
};
argc = parse_options(argc, argv, options, git_bisect_helper_usage, 0);
if (!next_all)
usage_with_options(git_bisect_helper_usage, options);
/* next-all */
return bisect_next_all(prefix);
}

Просмотреть файл

@ -1,5 +1,5 @@
/* /*
* Pickaxe * Blame
* *
* Copyright (c) 2006, Junio C Hamano * Copyright (c) 2006, Junio C Hamano
*/ */
@ -40,6 +40,10 @@ static int reverse;
static int blank_boundary; static int blank_boundary;
static int incremental; static int incremental;
static int xdl_opts = XDF_NEED_MINIMAL; 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; static struct string_list mailmap;
#ifndef DEBUG #ifndef DEBUG
@ -74,6 +78,7 @@ static unsigned blame_copy_score;
*/ */
struct origin { struct origin {
int refcnt; int refcnt;
struct origin *previous;
struct commit *commit; struct commit *commit;
mmfile_t file; mmfile_t file;
unsigned char blob_sha1[20]; 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) static void origin_decref(struct origin *o)
{ {
if (o && --o->refcnt <= 0) { if (o && --o->refcnt <= 0) {
if (o->previous)
origin_decref(o->previous);
free(o->file.ptr); free(o->file.ptr);
free(o); free(o);
} }
@ -866,7 +873,7 @@ static void find_copy_in_blob(struct scoreboard *sb,
* Prepare mmfile that contains only the lines in ent. * Prepare mmfile that contains only the lines in ent.
*/ */
cp = nth_line(sb, ent->lno); cp = nth_line(sb, ent->lno);
file_o.ptr = (char*) cp; file_o.ptr = (char *) cp;
cnt = ent->num_lines; cnt = ent->num_lines;
while (cnt && cp < sb->final_buf + sb->final_buf_size) { while (cnt && cp < sb->final_buf + sb->final_buf_size) {
@ -1198,6 +1205,10 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
struct origin *porigin = sg_origin[i]; struct origin *porigin = sg_origin[i];
if (!porigin) if (!porigin)
continue; continue;
if (!origin->previous) {
origin_incref(porigin);
origin->previous = porigin;
}
if (pass_blame_to_parent(sb, origin, porigin)) if (pass_blame_to_parent(sb, origin, porigin))
goto finish; goto finish;
} }
@ -1414,6 +1425,39 @@ static void write_filename_info(const char *path)
write_name_quoted(path, stdout, '\n'); write_name_quoted(path, stdout, '\n');
} }
/*
* 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 * The blame_entry is found to be guilty for the range. Mark it
* as such, and show it in incremental output. * 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", printf("%s %d %d %d\n",
sha1_to_hex(suspect->commit->object.sha1), sha1_to_hex(suspect->commit->object.sha1),
ent->s_lno + 1, ent->lno + 1, ent->num_lines); ent->s_lno + 1, ent->lno + 1, ent->num_lines);
if (!(suspect->commit->object.flags & METAINFO_SHOWN)) { emit_one_suspect_detail(suspect);
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");
}
write_filename_info(suspect->path); write_filename_info(suspect->path);
maybe_flush_or_die(stdout, "stdout"); 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) int show_raw_time)
{ {
static char time_buf[128]; static char time_buf[128];
time_t t = time; const char *time_str;
int minutes, tz; int time_len;
struct tm *tm; int tz;
if (show_raw_time) { if (show_raw_time) {
sprintf(time_buf, "%lu %s", time, tz_str); sprintf(time_buf, "%lu %s", time, tz_str);
return time_buf;
} }
else {
tz = atoi(tz_str); tz = atoi(tz_str);
minutes = tz < 0 ? -tz : tz; time_str = show_date(time, tz, blame_date_mode);
minutes = (minutes / 100)*60 + (minutes % 100); time_len = strlen(time_str);
minutes = tz < 0 ? -minutes : minutes; memcpy(time_buf, time_str, time_len);
t = time + minutes * 60; memset(time_buf + time_len, ' ', blame_date_width - time_len);
tm = gmtime(&t); }
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S ", tm);
strcat(time_buf, tz_str);
return time_buf; return time_buf;
} }
@ -1551,24 +1576,8 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
ent->s_lno + 1, ent->s_lno + 1,
ent->lno + 1, ent->lno + 1,
ent->num_lines); ent->num_lines);
if (!(suspect->commit->object.flags & METAINFO_SHOWN)) { if (emit_one_suspect_detail(suspect) ||
struct commit_info ci; (suspect->commit->object.flags & MORE_THAN_ONE_PATH))
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)
write_filename_info(suspect->path); write_filename_info(suspect->path);
cp = nth_line(sb, ent->lno); cp = nth_line(sb, ent->lno);
@ -1695,7 +1704,7 @@ static int prepare_lines(struct scoreboard *sb)
while (len--) { while (len--) {
if (bol) { if (bol) {
sb->lineno = xrealloc(sb->lineno, sb->lineno = xrealloc(sb->lineno,
sizeof(int* ) * (num + 1)); sizeof(int *) * (num + 1));
sb->lineno[num] = buf - sb->final_buf; sb->lineno[num] = buf - sb->final_buf;
bol = 0; bol = 0;
} }
@ -1705,7 +1714,7 @@ static int prepare_lines(struct scoreboard *sb)
} }
} }
sb->lineno = xrealloc(sb->lineno, sb->lineno = xrealloc(sb->lineno,
sizeof(int* ) * (num + incomplete + 1)); sizeof(int *) * (num + incomplete + 1));
sb->lineno[num + incomplete] = buf - sb->final_buf; sb->lineno[num + incomplete] = buf - sb->final_buf;
sb->num_lines = num + incomplete; sb->num_lines = num + incomplete;
return sb->num_lines; return sb->num_lines;
@ -1806,36 +1815,6 @@ static void sanity_check_refcnt(struct scoreboard *sb)
baa = 1; 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) { if (baa) {
int opt = 0160; int opt = 0160;
find_alignment(sb, &opt); find_alignment(sb, &opt);
@ -1910,7 +1889,7 @@ static const char *parse_loc(const char *spec,
return spec; return spec;
/* it could be a regexp of form /.../ */ /* it could be a regexp of form /.../ */
for (term = (char*) spec + 1; *term && *term != '/'; term++) { for (term = (char *) spec + 1; *term && *term != '/'; term++) {
if (*term == '\\') if (*term == '\\')
term++; term++;
} }
@ -1975,6 +1954,12 @@ static int git_blame_config(const char *var, const char *value, void *cb)
blank_boundary = git_config_bool(var, value); blank_boundary = git_config_bool(var, value);
return 0; 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); 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); git_config(git_blame_config, NULL);
init_revisions(&revs, NULL); init_revisions(&revs, NULL);
revs.date_mode = blame_date_mode;
save_commit_buffer = 0; save_commit_buffer = 0;
dashdash_pos = 0; dashdash_pos = 0;
@ -2267,8 +2254,35 @@ parse_done:
die("reading graft file %s failed: %s", die("reading graft file %s failed: %s",
revs_file, strerror(errno)); revs_file, strerror(errno));
if (cmd_is_annotate) if (cmd_is_annotate) {
output_option |= OUTPUT_ANNOTATE_COMPAT; 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)) if (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER))
opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE | opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |

Просмотреть файл

@ -32,18 +32,18 @@ static unsigned char head_sha1[20];
static int branch_use_color = -1; static int branch_use_color = -1;
static char branch_colors[][COLOR_MAXLEN] = { static char branch_colors[][COLOR_MAXLEN] = {
"\033[m", /* reset */ GIT_COLOR_RESET,
"", /* PLAIN (normal) */ GIT_COLOR_NORMAL, /* PLAIN */
"\033[31m", /* REMOTE (red) */ GIT_COLOR_RED, /* REMOTE */
"", /* LOCAL (normal) */ GIT_COLOR_NORMAL, /* LOCAL */
"\033[32m", /* CURRENT (green) */ GIT_COLOR_GREEN, /* CURRENT */
}; };
enum color_branch { enum color_branch {
COLOR_BRANCH_RESET = 0, BRANCH_COLOR_RESET = 0,
COLOR_BRANCH_PLAIN = 1, BRANCH_COLOR_PLAIN = 1,
COLOR_BRANCH_REMOTE = 2, BRANCH_COLOR_REMOTE = 2,
COLOR_BRANCH_LOCAL = 3, BRANCH_COLOR_LOCAL = 3,
COLOR_BRANCH_CURRENT = 4, BRANCH_COLOR_CURRENT = 4,
}; };
static enum merge_filter { 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) static int parse_branch_color_slot(const char *var, int ofs)
{ {
if (!strcasecmp(var+ofs, "plain")) if (!strcasecmp(var+ofs, "plain"))
return COLOR_BRANCH_PLAIN; return BRANCH_COLOR_PLAIN;
if (!strcasecmp(var+ofs, "reset")) if (!strcasecmp(var+ofs, "reset"))
return COLOR_BRANCH_RESET; return BRANCH_COLOR_RESET;
if (!strcasecmp(var+ofs, "remote")) if (!strcasecmp(var+ofs, "remote"))
return COLOR_BRANCH_REMOTE; return BRANCH_COLOR_REMOTE;
if (!strcasecmp(var+ofs, "local")) if (!strcasecmp(var+ofs, "local"))
return COLOR_BRANCH_LOCAL; return BRANCH_COLOR_LOCAL;
if (!strcasecmp(var+ofs, "current")) if (!strcasecmp(var+ofs, "current"))
return COLOR_BRANCH_CURRENT; return BRANCH_COLOR_CURRENT;
die("bad config variable '%s'", var); die("bad config variable '%s'", var);
} }
@ -121,11 +121,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
die("Couldn't look up commit object for HEAD"); die("Couldn't look up commit object for HEAD");
} }
for (i = 0; i < argc; i++, strbuf_release(&bname)) { for (i = 0; i < argc; i++, strbuf_release(&bname)) {
int len = strlen(argv[i]); strbuf_branchname(&bname, argv[i]);
if (interpret_nth_last_branch(argv[i], &bname) != len)
strbuf_add(&bname, argv[i], len);
if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) { if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) {
error("Cannot delete the branch '%s' " error("Cannot delete the branch '%s' "
"which you are currently on.", bname.buf); "which you are currently on.", bname.buf);
@ -188,7 +184,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
struct ref_item { struct ref_item {
char *name; char *name;
unsigned int kind; char *dest;
unsigned int kind, len;
struct commit *commit; struct commit *commit;
}; };
@ -200,22 +197,47 @@ struct ref_list {
int kinds; 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) 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_list *ref_list = (struct ref_list*)(cb_data);
struct ref_item *newitem; struct ref_item *newitem;
struct commit *commit; struct commit *commit;
int kind; int kind, i;
int len; 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 */ /* Detect kind */
if (!prefixcmp(refname, "refs/heads/")) { for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
kind = REF_LOCAL_BRANCH; prefix = ref_kind[i].prefix;
refname += 11; if (strncmp(refname, prefix, ref_kind[i].pfxlen))
} else if (!prefixcmp(refname, "refs/remotes/")) { continue;
kind = REF_REMOTE_BRANCH; kind = ref_kind[i].kind;
refname += 13; refname += ref_kind[i].pfxlen;
} else break;
}
if (ARRAY_SIZE(ref_kind) <= i)
return 0; return 0;
commit = lookup_commit_reference_gently(sha1, 1); commit = lookup_commit_reference_gently(sha1, 1);
@ -246,9 +268,14 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
newitem->name = xstrdup(refname); newitem->name = xstrdup(refname);
newitem->kind = kind; newitem->kind = kind;
newitem->commit = commit; newitem->commit = commit;
len = strlen(newitem->name); newitem->len = strlen(refname);
if (len > ref_list->maxwidth) newitem->dest = resolve_symref(orig_refname, prefix);
ref_list->maxwidth = len; /* 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; return 0;
} }
@ -257,8 +284,10 @@ static void free_ref_list(struct ref_list *ref_list)
{ {
int i; 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].name);
free(ref_list->list[i].dest);
}
free(ref_list->list); free(ref_list->list);
} }
@ -272,19 +301,30 @@ static int ref_cmp(const void *r1, const void *r2)
return strcmp(c1->name, c2->name); return strcmp(c1->name, c2->name);
} }
static void fill_tracking_info(struct strbuf *stat, const char *branch_name) static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
int show_upstream_ref)
{ {
int ours, theirs; int ours, theirs;
struct branch *branch = branch_get(branch_name); struct branch *branch = branch_get(branch_name);
if (!stat_tracking_info(branch, &ours, &theirs) || (!ours && !theirs)) if (!stat_tracking_info(branch, &ours, &theirs)) {
if (branch && branch->merge && branch->merge[0]->dst &&
show_upstream_ref)
strbuf_addf(stat, "[%s] ",
shorten_unambiguous_ref(branch->merge[0]->dst, 0));
return; return;
}
strbuf_addch(stat, '[');
if (show_upstream_ref)
strbuf_addf(stat, "%s: ",
shorten_unambiguous_ref(branch->merge[0]->dst, 0));
if (!ours) if (!ours)
strbuf_addf(stat, "[behind %d] ", theirs); strbuf_addf(stat, "behind %d] ", theirs);
else if (!theirs) else if (!theirs)
strbuf_addf(stat, "[ahead %d] ", ours); strbuf_addf(stat, "ahead %d] ", ours);
else else
strbuf_addf(stat, "[ahead %d, behind %d] ", ours, theirs); strbuf_addf(stat, "ahead %d, behind %d] ", ours, theirs);
} }
static int matches_merge_filter(struct commit *commit) static int matches_merge_filter(struct commit *commit)
@ -299,34 +339,46 @@ static int matches_merge_filter(struct commit *commit)
} }
static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, 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; char c;
int color; int color;
struct commit *commit = item->commit; struct commit *commit = item->commit;
struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
if (!matches_merge_filter(commit)) if (!matches_merge_filter(commit))
return; return;
switch (item->kind) { switch (item->kind) {
case REF_LOCAL_BRANCH: case REF_LOCAL_BRANCH:
color = COLOR_BRANCH_LOCAL; color = BRANCH_COLOR_LOCAL;
break; break;
case REF_REMOTE_BRANCH: case REF_REMOTE_BRANCH:
color = COLOR_BRANCH_REMOTE; color = BRANCH_COLOR_REMOTE;
break; break;
default: default:
color = COLOR_BRANCH_PLAIN; color = BRANCH_COLOR_PLAIN;
break; break;
} }
c = ' '; c = ' ';
if (current) { if (current) {
c = '*'; 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; struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
const char *sub = " **** invalid ref ****"; const char *sub = " **** invalid ref ****";
@ -338,30 +390,27 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
} }
if (item->kind == REF_LOCAL_BRANCH) if (item->kind == REF_LOCAL_BRANCH)
fill_tracking_info(&stat, item->name); fill_tracking_info(&stat, item->name, verbose > 1);
printf("%c %s%-*s%s %s %s%s\n", c, branch_get_color(color), strbuf_addf(&out, " %s %s%s",
maxwidth, item->name, find_unique_abbrev(item->commit->object.sha1, abbrev),
branch_get_color(COLOR_BRANCH_RESET), stat.buf, sub);
find_unique_abbrev(item->commit->object.sha1, abbrev),
stat.buf, sub);
strbuf_release(&stat); strbuf_release(&stat);
strbuf_release(&subject); 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) static int calc_maxwidth(struct ref_list *refs)
{ {
int i, l, w = 0; int i, w = 0;
for (i = 0; i < refs->index; i++) { for (i = 0; i < refs->index; i++) {
if (!matches_merge_filter(refs->list[i].commit)) if (!matches_merge_filter(refs->list[i].commit))
continue; continue;
l = strlen(refs->list[i].name); if (refs->list[i].len > w)
if (l > w) w = refs->list[i].len;
w = l;
} }
return w; return w;
} }
@ -397,11 +446,13 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
is_descendant_of(head_commit, with_commit)) { is_descendant_of(head_commit, with_commit)) {
struct ref_item item; struct ref_item item;
item.name = xstrdup("(no branch)"); item.name = xstrdup("(no branch)");
item.len = strlen(item.name);
item.kind = REF_LOCAL_BRANCH; item.kind = REF_LOCAL_BRANCH;
item.dest = NULL;
item.commit = head_commit; item.commit = head_commit;
if (strlen(item.name) > ref_list.maxwidth) if (item.len > ref_list.maxwidth)
ref_list.maxwidth = strlen(item.name); ref_list.maxwidth = item.len;
print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1); print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1, "");
free(item.name); free(item.name);
} }
@ -409,8 +460,11 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
int current = !detached && int current = !detached &&
(ref_list.list[i].kind == REF_LOCAL_BRANCH) && (ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
!strcmp(ref_list.list[i].name, head); !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, print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose,
abbrev, current); abbrev, current, prefix);
} }
free_ref_list(&ref_list); free_ref_list(&ref_list);
@ -421,22 +475,27 @@ static void rename_branch(const char *oldname, const char *newname, int force)
struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT; struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
unsigned char sha1[20]; unsigned char sha1[20];
struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT; struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
int recovery = 0;
if (!oldname) if (!oldname)
die("cannot rename the current branch while not on any."); die("cannot rename the current branch while not on any.");
strbuf_addf(&oldref, "refs/heads/%s", oldname); if (strbuf_check_branch_ref(&oldref, oldname)) {
/*
* Bad name --- this could be an attempt to rename a
* ref that we used to allow to be created by accident.
*/
if (resolve_ref(oldref.buf, sha1, 1, NULL))
recovery = 1;
else
die("Invalid branch name: '%s'", oldname);
}
if (check_ref_format(oldref.buf)) if (strbuf_check_branch_ref(&newref, newname))
die("Invalid branch name: %s", oldref.buf); die("Invalid branch name: '%s'", newname);
strbuf_addf(&newref, "refs/heads/%s", newname);
if (check_ref_format(newref.buf))
die("Invalid branch name: %s", newref.buf);
if (resolve_ref(newref.buf, sha1, 1, NULL) && !force) if (resolve_ref(newref.buf, sha1, 1, NULL) && !force)
die("A branch named '%s' already exists.", newname); die("A branch named '%s' already exists.", newref.buf + 11);
strbuf_addf(&logmsg, "Branch: renamed %s to %s", strbuf_addf(&logmsg, "Branch: renamed %s to %s",
oldref.buf, newref.buf); oldref.buf, newref.buf);
@ -445,6 +504,9 @@ static void rename_branch(const char *oldname, const char *newname, int force)
die("Branch rename failed"); die("Branch rename failed");
strbuf_release(&logmsg); strbuf_release(&logmsg);
if (recovery)
warning("Renamed a misnamed branch '%s' away", oldref.buf + 11);
/* no need to pass logmsg here as HEAD didn't really move */ /* no need to pass logmsg here as HEAD didn't really move */
if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL)) if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL))
die("Branch renamed to %s, but HEAD is not updated!", newname); die("Branch renamed to %s, but HEAD is not updated!", newname);
@ -485,7 +547,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
struct option options[] = { struct option options[] = {
OPT_GROUP("Generic options"), OPT_GROUP("Generic options"),
OPT__VERBOSE(&verbose), OPT__VERBOSE(&verbose),
OPT_SET_INT( 0 , "track", &track, "set up tracking mode (see git-pull(1))", OPT_SET_INT('t', "track", &track, "set up tracking mode (see git-pull(1))",
BRANCH_TRACK_EXPLICIT), BRANCH_TRACK_EXPLICIT),
OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"), OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"),
OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches", OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches",

Просмотреть файл

@ -5,9 +5,18 @@
#include "cache.h" #include "cache.h"
#include "refs.h" #include "refs.h"
#include "builtin.h" #include "builtin.h"
#include "strbuf.h"
int cmd_check_ref_format(int argc, const char **argv, const char *prefix) int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
{ {
if (argc == 3 && !strcmp(argv[1], "--branch")) {
struct strbuf sb = STRBUF_INIT;
if (strbuf_check_branch_ref(&sb, argv[2]))
die("'%s' is not a valid branch name", argv[2]);
printf("%s\n", sb.buf + 11);
exit(0);
}
if (argc != 2) if (argc != 2)
usage("git check-ref-format refname"); usage("git check-ref-format refname");
return !!check_ref_format(argv[1]); return !!check_ref_format(argv[1]);

Просмотреть файл

@ -124,7 +124,7 @@ static int checkout_file(const char *name, int prefix_length)
static void checkout_all(const char *prefix, int prefix_length) static void checkout_all(const char *prefix, int prefix_length)
{ {
int i, errs = 0; int i, errs = 0;
struct cache_entry* last_ce = NULL; struct cache_entry *last_ce = NULL;
for (i = 0; i < active_nr ; i++) { for (i = 0; i < active_nr ; i++) {
struct cache_entry *ce = active_cache[i]; struct cache_entry *ce = active_cache[i];
@ -278,7 +278,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
p = prefix_path(prefix, prefix_length, arg); p = prefix_path(prefix, prefix_length, arg);
checkout_file(p, prefix_length); checkout_file(p, prefix_length);
if (p < arg || p > arg + strlen(arg)) if (p < arg || p > arg + strlen(arg))
free((char*)p); free((char *)p);
} }
if (read_from_stdin) { if (read_from_stdin) {

Просмотреть файл

@ -179,7 +179,7 @@ static int checkout_merged(int pos, struct checkout *state)
/* /*
* NEEDSWORK: * NEEDSWORK:
* There is absolutely no reason to write this as a blob object * There is absolutely no reason to write this as a blob object
* and create a phoney cache entry just to leak. This hack is * and create a phony cache entry just to leak. This hack is
* primarily to get to the write_entry() machinery that massages * primarily to get to the write_entry() machinery that massages
* the contents to work-tree format and writes out which only * the contents to work-tree format and writes out which only
* allows it for a cache entry. The code in write_entry() needs * allows it for a cache entry. The code in write_entry() needs
@ -216,7 +216,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
newfd = hold_locked_index(lock_file, 1); newfd = hold_locked_index(lock_file, 1);
if (read_cache() < 0) if (read_cache_preload(pathspec) < 0)
return error("corrupt index file"); return error("corrupt index file");
if (source_tree) if (source_tree)
@ -293,6 +293,8 @@ static void show_local_changes(struct object *head)
init_revisions(&rev, NULL); init_revisions(&rev, NULL);
rev.abbrev = 0; rev.abbrev = 0;
rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS; 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); add_pending_object(&rev, head, NULL);
run_diff_index(&rev, 0); run_diff_index(&rev, 0);
} }
@ -349,16 +351,11 @@ struct branch_info {
static void setup_branch_path(struct branch_info *branch) static void setup_branch_path(struct branch_info *branch)
{ {
struct strbuf buf = STRBUF_INIT; struct strbuf buf = STRBUF_INIT;
int ret;
if ((ret = interpret_nth_last_branch(branch->name, &buf)) strbuf_branchname(&buf, branch->name);
&& ret == strlen(branch->name)) { if (strcmp(buf.buf, branch->name))
branch->name = xstrdup(buf.buf); branch->name = xstrdup(buf.buf);
strbuf_splice(&buf, 0, 0, "refs/heads/", 11); strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
} else {
strbuf_addstr(&buf, "refs/heads/");
strbuf_addstr(&buf, branch->name);
}
branch->path = strbuf_detach(&buf, NULL); branch->path = strbuf_detach(&buf, NULL);
} }
@ -369,7 +366,7 @@ static int merge_working_tree(struct checkout_opts *opts,
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
int newfd = hold_locked_index(lock_file, 1); int newfd = hold_locked_index(lock_file, 1);
if (read_cache() < 0) if (read_cache_preload(NULL) < 0)
return error("corrupt index file"); return error("corrupt index file");
if (opts->force) { if (opts->force) {
@ -403,7 +400,7 @@ static int merge_working_tree(struct checkout_opts *opts,
topts.verbose_update = !opts->quiet; topts.verbose_update = !opts->quiet;
topts.fn = twoway_merge; topts.fn = twoway_merge;
topts.dir = xcalloc(1, sizeof(*topts.dir)); topts.dir = xcalloc(1, sizeof(*topts.dir));
topts.dir->show_ignored = 1; topts.dir->flags |= DIR_SHOW_IGNORED;
topts.dir->exclude_per_dir = ".gitignore"; topts.dir->exclude_per_dir = ".gitignore";
tree = parse_tree_indirect(old->commit->object.sha1); tree = parse_tree_indirect(old->commit->object.sha1);
init_tree_desc(&trees[0], tree->buffer, tree->size); init_tree_desc(&trees[0], tree->buffer, tree->size);
@ -544,18 +541,10 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
parse_commit(new->commit); parse_commit(new->commit);
} }
/*
* If we were on a detached HEAD, but we are now moving to
* a new commit, we want to mention the old commit once more
* to remind the user that it might be lost.
*/
if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
describe_detached_head("Previous HEAD position was", old.commit);
if (!old.commit && !opts->force) { if (!old.commit && !opts->force) {
if (!opts->quiet) { if (!opts->quiet) {
fprintf(stderr, "warning: You appear to be on a branch yet to be born.\n"); warning("You appear to be on a branch yet to be born.");
fprintf(stderr, "warning: Forcing checkout of %s.\n", new->name); warning("Forcing checkout of %s.", new->name);
} }
opts->force = 1; opts->force = 1;
} }
@ -564,6 +553,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
if (ret) if (ret)
return ret; return ret;
/*
* If we were on a detached HEAD, but have now moved to
* a new commit, we want to mention the old commit once more
* to remind the user that it might be lost.
*/
if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
describe_detached_head("Previous HEAD position was", old.commit);
update_refs_for_switch(opts, &old, new); update_refs_for_switch(opts, &old, new);
ret = post_checkout_hook(old.commit, new->commit, 1); ret = post_checkout_hook(old.commit, new->commit, 1);
@ -734,12 +731,11 @@ no_reference:
if (opts.new_branch) { if (opts.new_branch) {
struct strbuf buf = STRBUF_INIT; struct strbuf buf = STRBUF_INIT;
strbuf_addstr(&buf, "refs/heads/"); if (strbuf_check_branch_ref(&buf, opts.new_branch))
strbuf_addstr(&buf, opts.new_branch); die("git checkout: we do not like '%s' as a branch name.",
opts.new_branch);
if (!get_sha1(buf.buf, rev)) if (!get_sha1(buf.buf, rev))
die("git checkout: branch %s already exists", opts.new_branch); die("git checkout: branch %s already exists", opts.new_branch);
if (check_ref_format(buf.buf))
die("git checkout: we do not like '%s' as a branch name.", opts.new_branch);
strbuf_release(&buf); strbuf_release(&buf);
} }

Просмотреть файл

@ -60,7 +60,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
memset(&dir, 0, sizeof(dir)); memset(&dir, 0, sizeof(dir));
if (ignored_only) if (ignored_only)
dir.show_ignored = 1; dir.flags |= DIR_SHOW_IGNORED;
if (ignored && ignored_only) if (ignored && ignored_only)
die("-x and -X cannot be used together"); 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; " die("clean.requireForce%s set and -n or -f not given; "
"refusing to clean", config_set ? "" : " not"); "refusing to clean", config_set ? "" : " not");
dir.show_other_directories = 1; dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
if (!ignored) if (!ignored)
setup_standard_excludes(&dir); setup_standard_excludes(&dir);

Просмотреть файл

@ -20,6 +20,9 @@
#include "dir.h" #include "dir.h"
#include "pack-refs.h" #include "pack-refs.h"
#include "sigchain.h" #include "sigchain.h"
#include "branch.h"
#include "remote.h"
#include "run-command.h"
/* /*
* Overall FIXMEs: * Overall FIXMEs:
@ -101,11 +104,12 @@ static char *get_repo_path(const char *repo, int *is_bundle)
static char *guess_dir_name(const char *repo, int is_bundle, int is_bare) static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
{ {
const char *end = repo + strlen(repo), *start; const char *end = repo + strlen(repo), *start;
char *dir;
/* /*
* Strip trailing slashes and /.git * Strip trailing spaces, slashes and /.git
*/ */
while (repo < end && is_dir_sep(end[-1])) while (repo < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
end--; end--;
if (end - repo > 5 && is_dir_sep(end[-5]) && if (end - repo > 5 && is_dir_sep(end[-5]) &&
!strncmp(end - 4, ".git", 4)) { !strncmp(end - 4, ".git", 4)) {
@ -137,10 +141,33 @@ static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
if (is_bare) { if (is_bare) {
struct strbuf result = STRBUF_INIT; struct strbuf result = STRBUF_INIT;
strbuf_addf(&result, "%.*s.git", (int)(end - start), start); strbuf_addf(&result, "%.*s.git", (int)(end - start), start);
return strbuf_detach(&result, 0); dir = strbuf_detach(&result, 0);
} else
dir = xstrndup(start, end - start);
/*
* Replace sequences of 'control' characters and whitespace
* with one ascii space, remove leading and trailing spaces.
*/
if (*dir) {
char *out = dir;
int prev_space = 1 /* strip leading whitespace */;
for (end = dir; *end; ++end) {
char ch = *end;
if ((unsigned char)ch < '\x20')
ch = '\x20';
if (isspace(ch)) {
if (prev_space)
continue;
prev_space = 1;
} else
prev_space = 0;
*out++ = ch;
}
*out = '\0';
if (out > dir && prev_space)
out[-1] = '\0';
} }
return dir;
return xstrndup(start, end - start);
} }
static void strip_trailing_slashes(char *dir) static void strip_trailing_slashes(char *dir)
@ -225,7 +252,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
} }
if (unlink(dest->buf) && errno != ENOENT) if (unlink(dest->buf) && errno != ENOENT)
die("failed to unlink %s", dest->buf); die("failed to unlink %s: %s",
dest->buf, strerror(errno));
if (!option_no_hardlinks) { if (!option_no_hardlinks) {
if (!link(src->buf, dest->buf)) if (!link(src->buf, dest->buf))
continue; continue;
@ -267,7 +295,7 @@ static const struct ref *clone_local(const char *src_repo,
static const char *junk_work_tree; static const char *junk_work_tree;
static const char *junk_git_dir; static const char *junk_git_dir;
pid_t junk_pid; static pid_t junk_pid;
static void remove_junk(void) static void remove_junk(void)
{ {
@ -293,43 +321,6 @@ static void remove_junk_on_signal(int signo)
raise(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, static struct ref *write_remote_refs(const struct ref *refs,
struct refspec *refspec, const char *reflog) struct refspec *refspec, const char *reflog)
{ {
@ -350,23 +341,8 @@ static struct ref *write_remote_refs(const struct ref *refs,
return local_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 cmd_clone(int argc, const char **argv, const char *prefix)
{ {
int use_local_hardlinks = 1;
int use_separate_remote = 1;
int is_bundle = 0; int is_bundle = 0;
struct stat buf; struct stat buf;
const char *repo_name, *repo, *work_tree, *git_dir; const char *repo_name, *repo, *work_tree, *git_dir;
@ -377,8 +353,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT; struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
struct transport *transport = NULL; struct transport *transport = NULL;
char *src_ref_prefix = "refs/heads/"; char *src_ref_prefix = "refs/heads/";
int err = 0;
struct refspec refspec; struct refspec *refspec;
const char *fetch_pattern;
junk_pid = getpid(); junk_pid = getpid();
@ -388,9 +366,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (argc == 0) if (argc == 0)
die("You must specify a repository to clone."); die("You must specify a repository to clone.");
if (option_no_hardlinks)
use_local_hardlinks = 0;
if (option_mirror) if (option_mirror)
option_bare = 1; option_bare = 1;
@ -399,7 +374,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
die("--bare and --origin %s options are incompatible.", die("--bare and --origin %s options are incompatible.",
option_origin); option_origin);
option_no_checkout = 1; option_no_checkout = 1;
use_separate_remote = 0;
} }
if (!option_origin) if (!option_origin)
@ -457,7 +431,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
atexit(remove_junk); atexit(remove_junk);
sigchain_push_common(remove_junk_on_signal); 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) if (safe_create_leading_directories_const(git_dir) < 0)
die("could not create leading directories of '%s'", git_dir); die("could not create leading directories of '%s'", git_dir);
@ -487,8 +461,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin); 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) { if (option_mirror || !option_bare) {
/* Configure the remote */ /* 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) { if (option_mirror) {
strbuf_addf(&key, "remote.%s.mirror", option_origin); strbuf_addf(&key, "remote.%s.mirror", option_origin);
git_config_set(key.buf, "true"); git_config_set(key.buf, "true");
@ -497,19 +477,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
strbuf_addf(&key, "remote.%s.url", option_origin); strbuf_addf(&key, "remote.%s.url", option_origin);
git_config_set(key.buf, repo); 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(&key);
strbuf_reset(&value);
} }
refspec.force = 0; fetch_pattern = value.buf;
refspec.pattern = 1; refspec = parse_fetch_refspec(1, &fetch_pattern);
refspec.src = src_ref_prefix;
refspec.dst = branch_top.buf; strbuf_reset(&value);
if (path && !is_bundle) if (path && !is_bundle)
refs = clone_local(path, git_dir); refs = clone_local(path, git_dir);
@ -543,9 +517,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (refs) { if (refs) {
clear_extra_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 { else {
warning("You appear to have cloned an empty repository."); warning("You appear to have cloned an empty repository.");
@ -553,7 +528,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
remote_head = NULL; remote_head = NULL;
option_no_checkout = 1; option_no_checkout = 1;
if (!option_bare) if (!option_bare)
install_branch_config("master", option_origin, install_branch_config(0, "master", option_origin,
"refs/heads/master"); "refs/heads/master");
} }
@ -583,7 +558,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
head_points_at->peer_ref->name, head_points_at->peer_ref->name,
reflog_msg.buf); reflog_msg.buf);
install_branch_config(head, option_origin, install_branch_config(0, head, option_origin,
head_points_at->name); head_points_at->name);
} }
} else if (remote_head) { } else if (remote_head) {
@ -631,6 +606,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (write_cache(fd, active_cache, active_nr) || if (write_cache(fd, active_cache, active_nr) ||
commit_locked_index(lock_file)) commit_locked_index(lock_file))
die("unable to write new index 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); strbuf_release(&reflog_msg);
@ -638,5 +616,5 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
strbuf_release(&key); strbuf_release(&key);
strbuf_release(&value); strbuf_release(&value);
junk_pid = 0; junk_pid = 0;
return 0; return err;
} }

Просмотреть файл

@ -1,9 +1,12 @@
#include "builtin.h" #include "builtin.h"
#include "cache.h" #include "cache.h"
#include "color.h" #include "color.h"
#include "parse-options.h"
static const char git_config_set_usage[] = static const char *const builtin_config_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]"; "git config [options]",
NULL
};
static char *key; static char *key;
static regex_t *key_regexp; static regex_t *key_regexp;
@ -16,7 +19,67 @@ static int seen;
static char delim = '='; static char delim = '=';
static char key_delim = ' '; static char key_delim = ' ';
static char term = '\n'; 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) 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; 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]; char value[256];
const char *vptr = value; const char *vptr = value;
@ -49,11 +112,11 @@ static int show_config(const char* key_, const char* value_, void *cb)
} }
if (seen && !do_all) if (seen && !do_all)
dup_error = 1; dup_error = 1;
if (type == T_INT) if (types == TYPE_INT)
sprintf(value, "%d", git_config_int(key_, value_?value_:"")); 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"; 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; int is_bool, v;
v = git_config_bool_or_int(key_, value_, &is_bool); v = git_config_bool_or_int(key_, value_, &is_bool);
if (is_bool) if (is_bool)
@ -74,7 +137,7 @@ static int show_config(const char* key_, const char* value_, void *cb)
return 0; 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; int ret = -1;
char *tl; char *tl;
@ -152,18 +215,18 @@ static char *normalize_value(const char *key, const char *value)
if (!value) if (!value)
return NULL; return NULL;
if (type == T_RAW) if (types == 0)
normalized = xstrdup(value); normalized = xstrdup(value);
else { else {
normalized = xmalloc(64); normalized = xmalloc(64);
if (type == T_INT) { if (types == TYPE_INT) {
int v = git_config_int(key, value); int v = git_config_int(key, value);
sprintf(normalized, "%d", v); sprintf(normalized, "%d", v);
} }
else if (type == T_BOOL) else if (types == TYPE_BOOL)
sprintf(normalized, "%s", sprintf(normalized, "%s",
git_config_bool(key, value) ? "true" : "false"); 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; int is_bool, v;
v = git_config_bool_or_int(key, value, &is_bool); v = git_config_bool_or_int(key, value, &is_bool);
if (!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 int get_color_found;
static const char *get_color_slot; static const char *get_color_slot;
static const char *get_colorbool_slot;
static char parsed_color[COLOR_MAXLEN]; static char parsed_color[COLOR_MAXLEN];
static int git_get_color_config(const char *var, const char *value, void *cb) 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; 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; get_color_found = 0;
parsed_color[0] = '\0'; parsed_color[0] = '\0';
git_config(git_get_color_config, NULL); 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); color_parse(def_color, "command line", parsed_color);
fputs(parsed_color, stdout); fputs(parsed_color, stdout);
return 0;
} }
static int stdout_is_tty; 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, static int git_get_colorbool_config(const char *var, const char *value,
void *cb) void *cb)
{ {
if (!strcmp(var, get_color_slot)) { if (!strcmp(var, get_colorbool_slot)) {
get_colorbool_found = get_colorbool_found =
git_config_colorbool(var, value, stdout_is_tty); git_config_colorbool(var, value, stdout_is_tty);
} }
@ -246,183 +288,190 @@ static int git_get_colorbool_config(const char *var, const char *value,
return 0; 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_colorbool_found = -1;
get_diff_color_found = -1; get_diff_color_found = -1;
get_color_slot = argv[0];
git_config(git_get_colorbool_config, NULL); git_config(git_get_colorbool_config, NULL);
if (get_colorbool_found < 0) { 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; get_colorbool_found = get_diff_color_found;
if (get_colorbool_found < 0) if (get_colorbool_found < 0)
get_colorbool_found = git_use_color_default; get_colorbool_found = git_use_color_default;
} }
if (argc == 1) { if (print) {
return get_colorbool_found ? 0 : 1;
} else {
printf("%s\n", get_colorbool_found ? "true" : "false"); printf("%s\n", get_colorbool_found ? "true" : "false");
return 0; 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; int nongit;
char* value; char *value;
const char *file = setup_git_directory_gently(&nongit); const char *prefix = setup_git_directory_gently(&nongit);
config_exclusive_filename = getenv(CONFIG_ENVIRONMENT); config_exclusive_filename = getenv(CONFIG_ENVIRONMENT);
while (1 < argc) { argc = parse_options(argc, argv, builtin_config_options, builtin_config_usage,
if (!strcmp(argv[1], "--int")) PARSE_OPT_STOP_AT_NON_OPTION);
type = T_INT;
else if (!strcmp(argv[1], "--bool")) if (use_global_config + use_system_config + !!given_config_file > 1) {
type = T_BOOL; error("only one config file at a time.");
else if (!strcmp(argv[1], "--bool-or-int")) usage_with_options(builtin_config_usage, builtin_config_options);
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) { if (use_global_config) {
case 2: char *home = getenv("HOME");
return get_value(argv[1], NULL); if (home) {
case 3: char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
if (!strcmp(argv[1], "--unset")) config_exclusive_filename = user_config;
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);
} else { } else {
value = normalize_value(argv[1], argv[2]); die("$HOME not set");
return git_config_set(argv[1], value);
} }
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);
}
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);
}
case 1:
default:
usage(git_config_set_usage);
} }
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);
}
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)");
}
}
else if (actions == ACTION_EDIT) {
check_argc(argc, 0, 0);
if (!config_exclusive_filename && nongit)
die("not in a git directory");
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; return 0;
} }

Просмотреть файл

@ -334,7 +334,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
die("--long is incompatible with --abbrev=0"); die("--long is incompatible with --abbrev=0");
if (contains) { if (contains) {
const char **args = xmalloc((7 + argc) * sizeof(char*)); const char **args = xmalloc((7 + argc) * sizeof(char *));
int i = 0; int i = 0;
args[i++] = "name-rev"; args[i++] = "name-rev";
args[i++] = "--name-only"; args[i++] = "--name-only";
@ -349,7 +349,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
args[i++] = s; args[i++] = s;
} }
} }
memcpy(args + i, argv, argc * sizeof(char*)); memcpy(args + i, argv, argc * sizeof(char *));
args[i + argc] = NULL; args[i + argc] = NULL;
return cmd_name_rev(i + argc, args, prefix); return cmd_name_rev(i + argc, args, prefix);
} }

Просмотреть файл

@ -102,7 +102,6 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
init_revisions(opt, prefix); init_revisions(opt, prefix);
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
nr_sha1 = 0;
opt->abbrev = 0; opt->abbrev = 0;
opt->diff = 1; opt->diff = 1;
argc = setup_revisions(argc, argv, opt, NULL); argc = setup_revisions(argc, argv, opt, NULL);

Просмотреть файл

@ -221,7 +221,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
if (message) if (message)
message += 2; message += 2;
if (commit->parents) { if (commit->parents &&
get_object_mark(&commit->parents->item->object) != 0) {
parse_commit(commit->parents->item); parse_commit(commit->parents->item);
diff_tree_sha1(commit->parents->item->tree->object.sha1, diff_tree_sha1(commit->parents->item->tree->object.sha1,
commit->tree->object.sha1, "", &rev->diffopt); commit->tree->object.sha1, "", &rev->diffopt);

Просмотреть файл

@ -112,7 +112,7 @@ static void mark_common(struct commit *commit,
Get the next rev to send, ignoring the common. Get the next rev to send, ignoring the common.
*/ */
static const unsigned char* get_rev(void) static const unsigned char *get_rev(void)
{ {
struct commit *commit = NULL; struct commit *commit = NULL;
@ -217,9 +217,8 @@ static int find_common(int fd[2], unsigned char *result_sha1,
if (args.depth > 0) { if (args.depth > 0) {
char line[1024]; char line[1024];
unsigned char sha1[20]; 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 (!prefixcmp(line, "shallow ")) {
if (get_sha1_hex(line + 8, sha1)) if (get_sha1_hex(line + 8, sha1))
die("invalid shallow line: %s", line); die("invalid shallow line: %s", line);
@ -484,7 +483,7 @@ static int sideband_demux(int fd, void *data)
{ {
int *xd = 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) static int get_pack(int xd[2], char **pack_lockfile)
@ -612,7 +611,7 @@ static struct ref *do_fetch_pack(int fd[2],
/* When cloning, it is not unusual to have /* When cloning, it is not unusual to have
* no common commit. * no common commit.
*/ */
fprintf(stderr, "warning: no common commits\n"); warning("no common commits");
if (get_pack(fd, pack_lockfile)) if (get_pack(fd, pack_lockfile))
die("git fetch-pack: fetch failed."); die("git fetch-pack: fetch failed.");
@ -812,15 +811,13 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
int fd; int fd;
mtime.sec = st.st_mtime; mtime.sec = st.st_mtime;
#ifdef USE_NSEC mtime.nsec = ST_MTIME_NSEC(st);
mtime.usec = st.st_mtim.usec;
#endif
if (stat(shallow, &st)) { if (stat(shallow, &st)) {
if (mtime.sec) if (mtime.sec)
die("shallow file was removed during fetch"); die("shallow file was removed during fetch");
} else if (st.st_mtime != mtime.sec } else if (st.st_mtime != mtime.sec
#ifdef USE_NSEC #ifdef USE_NSEC
|| st.st_mtim.usec != mtime.usec || ST_MTIME_NSEC(st) != mtime.nsec
#endif #endif
) )
die("shallow file was changed during fetch"); die("shallow file was changed during fetch");
@ -828,7 +825,7 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
fd = hold_lock_file_for_update(&lock, shallow, fd = hold_lock_file_for_update(&lock, shallow,
LOCK_DIE_ON_ERROR); LOCK_DIE_ON_ERROR);
if (!write_shallow_commits(fd, 0)) { if (!write_shallow_commits(fd, 0)) {
unlink(shallow); unlink_or_warn(shallow);
rollback_lock_file(&lock); rollback_lock_file(&lock);
} else { } else {
commit_lock_file(&lock); commit_lock_file(&lock);

Просмотреть файл

@ -197,11 +197,7 @@ static int update_local_ref(struct ref *ref,
struct commit *current = NULL, *updated; struct commit *current = NULL, *updated;
enum object_type type; enum object_type type;
struct branch *current_branch = branch_get(NULL); struct branch *current_branch = branch_get(NULL);
const char *pretty_ref = ref->name + ( const char *pretty_ref = prettify_refname(ref->name);
!prefixcmp(ref->name, "refs/heads/") ? 11 :
!prefixcmp(ref->name, "refs/tags/") ? 10 :
!prefixcmp(ref->name, "refs/remotes/") ? 13 :
0);
*display = 0; *display = 0;
type = sha1_object_info(ref->new_sha1, NULL); type = sha1_object_info(ref->new_sha1, NULL);
@ -293,7 +289,7 @@ static int update_local_ref(struct ref *ref,
} }
} }
static int store_updated_refs(const char *url, const char *remote_name, static int store_updated_refs(const char *raw_url, const char *remote_name,
struct ref *ref_map) struct ref *ref_map)
{ {
FILE *fp; FILE *fp;
@ -302,11 +298,13 @@ static int store_updated_refs(const char *url, const char *remote_name,
char note[1024]; char note[1024];
const char *what, *kind; const char *what, *kind;
struct ref *rm; struct ref *rm;
char *filename = git_path("FETCH_HEAD"); char *url, *filename = git_path("FETCH_HEAD");
fp = fopen(filename, "a"); fp = fopen(filename, "a");
if (!fp) if (!fp)
return error("cannot open %s: %s\n", filename, strerror(errno)); return error("cannot open %s: %s\n", filename, strerror(errno));
url = transport_anonymize_url(raw_url);
for (rm = ref_map; rm; rm = rm->next) { for (rm = ref_map; rm; rm = rm->next) {
struct ref *ref = NULL; struct ref *ref = NULL;
@ -357,12 +355,18 @@ static int store_updated_refs(const char *url, const char *remote_name,
kind); kind);
note_len += sprintf(note + note_len, "'%s' of ", what); note_len += sprintf(note + note_len, "'%s' of ", what);
} }
note_len += sprintf(note + note_len, "%.*s", url_len, url); note[note_len] = '\0';
fprintf(fp, "%s\t%s\t%s\n", fprintf(fp, "%s\t%s\t%s",
sha1_to_hex(commit ? commit->object.sha1 : sha1_to_hex(commit ? commit->object.sha1 :
rm->old_sha1), rm->old_sha1),
rm->merge ? "" : "not-for-merge", rm->merge ? "" : "not-for-merge",
note); note);
for (i = 0; i < url_len; ++i)
if ('\n' == url[i])
fputs("\\n", fp);
else
fputc(url[i], fp);
fputc('\n', fp);
if (ref) if (ref)
rc |= update_local_ref(ref, what, note); rc |= update_local_ref(ref, what, note);
@ -380,6 +384,7 @@ static int store_updated_refs(const char *url, const char *remote_name,
fprintf(stderr, " %s\n", note); fprintf(stderr, " %s\n", note);
} }
} }
free(url);
fclose(fp); fclose(fp);
if (rc & 2) if (rc & 2)
error("some local refs could not be updated; try running\n" error("some local refs could not be updated; try running\n"
@ -544,7 +549,8 @@ static void check_not_current_branch(struct ref *ref_map)
for (; ref_map; ref_map = ref_map->next) for (; ref_map; ref_map = ref_map->next)
if (ref_map->peer_ref && !strcmp(current_branch->refname, if (ref_map->peer_ref && !strcmp(current_branch->refname,
ref_map->peer_ref->name)) 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, static int do_fetch(struct transport *transport,

Просмотреть файл

@ -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 fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
int limit = 20, i = 0, pos = 0; int limit = 20, i = 0, pos = 0;
char line[1024]; char *sep = "";
char *p = line, *sep = "";
unsigned char head_sha1[20]; unsigned char head_sha1[20];
const char *current_branch; const char *current_branch;
@ -271,9 +270,8 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
/* get a line */ /* get a line */
while (pos < in->len) { while (pos < in->len) {
int len; int len;
char *newline; char *newline, *p = in->buf + pos;
p = in->buf + pos;
newline = strchr(p, '\n'); newline = strchr(p, '\n');
len = newline ? newline - p : strlen(p); len = newline ? newline - p : strlen(p);
pos += len + !!newline; pos += len + !!newline;

Просмотреть файл

@ -8,6 +8,7 @@
#include "blob.h" #include "blob.h"
#include "quote.h" #include "quote.h"
#include "parse-options.h" #include "parse-options.h"
#include "remote.h"
/* Quoting styles */ /* Quoting styles */
#define QUOTE_NONE 0 #define QUOTE_NONE 0
@ -66,6 +67,7 @@ static struct {
{ "subject" }, { "subject" },
{ "body" }, { "body" },
{ "contents" }, { "contents" },
{ "upstream" },
}; };
/* /*
@ -337,8 +339,11 @@ static const char *copy_name(const char *buf)
static const char *copy_email(const char *buf) static const char *copy_email(const char *buf)
{ {
const char *email = strchr(buf, '<'); const char *email = strchr(buf, '<');
const char *eoemail = strchr(email, '>'); const char *eoemail;
if (!email || !eoemail) if (!email)
return "";
eoemail = strchr(email, '>');
if (!eoemail)
return ""; return "";
return xmemdupz(email, eoemail + 1 - email); return xmemdupz(email, eoemail + 1 - email);
} }
@ -543,109 +548,6 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v
} }
} }
/*
* generate a format suitable for scanf from a ref_rev_parse_rules
* rule, that is replace the "%.*s" spec with a "%s" spec
*/
static void gen_scanf_fmt(char *scanf_fmt, const char *rule)
{
char *spec;
spec = strstr(rule, "%.*s");
if (!spec || strstr(spec + 4, "%.*s"))
die("invalid rule in ref_rev_parse_rules: %s", rule);
/* copy all until spec */
strncpy(scanf_fmt, rule, spec - rule);
scanf_fmt[spec - rule] = '\0';
/* copy new spec */
strcat(scanf_fmt, "%s");
/* copy remaining rule */
strcat(scanf_fmt, spec + 4);
return;
}
/*
* Shorten the refname to an non-ambiguous form
*/
static char *get_short_ref(struct refinfo *ref)
{
int i;
static char **scanf_fmts;
static int nr_rules;
char *short_name;
/* pre generate scanf formats from ref_rev_parse_rules[] */
if (!nr_rules) {
size_t total_len = 0;
/* the rule list is NULL terminated, count them first */
for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
/* no +1 because strlen("%s") < strlen("%.*s") */
total_len += strlen(ref_rev_parse_rules[nr_rules]);
scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
total_len = 0;
for (i = 0; i < nr_rules; i++) {
scanf_fmts[i] = (char *)&scanf_fmts[nr_rules]
+ total_len;
gen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);
total_len += strlen(ref_rev_parse_rules[i]);
}
}
/* bail out if there are no rules */
if (!nr_rules)
return ref->refname;
/* buffer for scanf result, at most ref->refname must fit */
short_name = xstrdup(ref->refname);
/* skip first rule, it will always match */
for (i = nr_rules - 1; i > 0 ; --i) {
int j;
int short_name_len;
if (1 != sscanf(ref->refname, scanf_fmts[i], short_name))
continue;
short_name_len = strlen(short_name);
/*
* check if the short name resolves to a valid ref,
* but use only rules prior to the matched one
*/
for (j = 0; j < i; j++) {
const char *rule = ref_rev_parse_rules[j];
unsigned char short_objectname[20];
char refname[PATH_MAX];
/*
* the short name is ambiguous, if it resolves
* (with this previous rule) to a valid ref
* read_ref() returns 0 on success
*/
mksnpath(refname, sizeof(refname),
rule, short_name_len, short_name);
if (!read_ref(refname, short_objectname))
break;
}
/*
* short name is non-ambiguous if all previous rules
* haven't resolved to a valid ref
*/
if (j == i)
return short_name;
}
free(short_name);
return ref->refname;
}
/* /*
* Parse the object referred by ref, and grab needed value. * Parse the object referred by ref, and grab needed value.
*/ */
@ -672,32 +574,50 @@ static void populate_value(struct refinfo *ref)
const char *name = used_atom[i]; const char *name = used_atom[i];
struct atom_value *v = &ref->value[i]; struct atom_value *v = &ref->value[i];
int deref = 0; int deref = 0;
const char *refname;
const char *formatp;
if (*name == '*') { if (*name == '*') {
deref = 1; deref = 1;
name++; name++;
} }
if (!prefixcmp(name, "refname")) {
const char *formatp = strchr(name, ':');
const char *refname = ref->refname;
/* look for "short" refname format */ if (!prefixcmp(name, "refname"))
if (formatp) { refname = ref->refname;
formatp++; else if(!prefixcmp(name, "upstream")) {
if (!strcmp(formatp, "short")) struct branch *branch;
refname = get_short_ref(ref); /* only local branches may have an upstream */
else if (prefixcmp(ref->refname, "refs/heads/"))
die("unknown refname format %s", continue;
formatp); branch = branch_get(ref->refname + 11);
}
if (!deref) if (!branch || !branch->merge || !branch->merge[0] ||
v->s = refname; !branch->merge[0]->dst)
else { continue;
int len = strlen(refname); refname = branch->merge[0]->dst;
char *s = xmalloc(len + 4); }
sprintf(s, "%s^{}", refname); else
v->s = s; continue;
}
formatp = strchr(name, ':');
/* look for "short" refname format */
if (formatp) {
formatp++;
if (!strcmp(formatp, "short"))
refname = shorten_unambiguous_ref(refname,
warn_ambiguous_refs);
else
die("unknown %.*s format %s",
(int)(formatp - name), name, formatp);
}
if (!deref)
v->s = refname;
else {
int len = strlen(refname);
char *s = xmalloc(len + 4);
sprintf(s, "%s^{}", refname);
v->s = s;
} }
} }
@ -943,7 +863,6 @@ static int opt_parse_sort(const struct option *opt, const char *arg, int unset)
return -1; return -1;
*sort_tail = s = xcalloc(1, sizeof(*s)); *sort_tail = s = xcalloc(1, sizeof(*s));
sort_tail = &s->next;
if (*arg == '-') { if (*arg == '-') {
s->reverse = 1; s->reverse = 1;
@ -1002,6 +921,9 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
sort = default_sort(); sort = default_sort();
sort_atom_limit = used_atom_cnt; sort_atom_limit = used_atom_cnt;
/* for warn_ambiguous_refs */
git_config(git_default_config, NULL);
memset(&cbdata, 0, sizeof(cbdata)); memset(&cbdata, 0, sizeof(cbdata));
cbdata.grab_pattern = argv; cbdata.grab_pattern = argv;
for_each_ref(grab_single_ref, &cbdata); for_each_ref(grab_single_ref, &cbdata);

Просмотреть файл

@ -23,7 +23,7 @@ static const char * const builtin_gc_usage[] = {
}; };
static int pack_refs = 1; 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_threshold = 6700;
static int gc_auto_pack_limit = 50; static int gc_auto_pack_limit = 50;
static const char *prune_expire = "2.weeks.ago"; 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) { if (aggressive) {
append_option(argv_repack, "-f", MAX_ADD); append_option(argv_repack, "-f", MAX_ADD);
append_option(argv_repack, "--depth=250", MAX_ADD);
if (aggressive_window > 0) { if (aggressive_window > 0) {
sprintf(buf, "--window=%d", aggressive_window); sprintf(buf, "--window=%d", aggressive_window);
append_option(argv_repack, buf, MAX_ADD); append_option(argv_repack, buf, MAX_ADD);

Просмотреть файл

@ -10,6 +10,7 @@
#include "tag.h" #include "tag.h"
#include "tree-walk.h" #include "tree-walk.h"
#include "builtin.h" #include "builtin.h"
#include "parse-options.h"
#include "grep.h" #include "grep.h"
#ifndef NO_EXTERNAL_GREP #ifndef NO_EXTERNAL_GREP
@ -20,7 +21,29 @@
#endif #endif
#endif #endif
static int builtin_grep; static char const * const grep_usage[] = {
"git grep [options] [-e] <pattern> [<rev>...] [[--] path...]",
NULL
};
static int grep_config(const char *var, const char *value, void *cb)
{
struct grep_opt *opt = cb;
if (!strcmp(var, "color.grep")) {
opt->color = git_config_colorbool(var, value, -1);
return 0;
}
if (!strcmp(var, "color.grep.external"))
return git_config_string(&(opt->color_external), var, value);
if (!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; * git grep pathspecs are somewhat different from diff-tree pathspecs;
@ -269,6 +292,21 @@ static int flush_grep(struct grep_opt *opt,
return status; 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) static int external_grep(struct grep_opt *opt, const char **paths, int cached)
{ {
int i, nr, argc, hit, len, status; int i, nr, argc, hit, len, status;
@ -339,6 +377,23 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
push_arg("-e"); push_arg("-e");
push_arg(p->pattern); 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; hit = 0;
argc = nr; argc = nr;
@ -381,7 +436,8 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
} }
#endif #endif
static int grep_cache(struct grep_opt *opt, const char **paths, int cached) static int grep_cache(struct grep_opt *opt, const char **paths, int cached,
int external_grep_allowed)
{ {
int hit = 0; int hit = 0;
int nr; int nr;
@ -393,7 +449,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
* we grep through the checked-out files. It tends to * we grep through the checked-out files. It tends to
* be a lot more optimized * be a lot more optimized
*/ */
if (!cached && !builtin_grep) { if (!cached && external_grep_allowed) {
hit = external_grep(opt, paths, cached); hit = external_grep(opt, paths, cached);
if (hit >= 0) if (hit >= 0)
return hit; return hit;
@ -509,25 +565,182 @@ static int grep_object(struct grep_opt *opt, const char **paths,
die("unable to grep from object of type %s", typename(obj->type)); die("unable to grep from object of type %s", typename(obj->type));
} }
static const char builtin_grep_usage[] = static int context_callback(const struct option *opt, const char *arg,
"git grep <option>* [-e] <pattern> <rev>* [[--] <path>...]"; int unset)
{
struct grep_opt *grep_opt = opt->value;
int value;
const char *endp;
static const char emsg_invalid_context_len[] = if (unset) {
"%s: invalid context length argument"; grep_opt->pre_context = grep_opt->post_context = 0;
static const char emsg_missing_context_len[] = return 0;
"missing context length argument"; }
static const char emsg_missing_argument[] = value = strtol(arg, (char **)&endp, 10);
"option requires an argument -%s"; if (*endp) {
return error("switch `%c' expects a numerical value",
opt->short_name);
}
grep_opt->pre_context = grep_opt->post_context = value;
return 0;
}
static int file_callback(const struct option *opt, const char *arg, int unset)
{
struct grep_opt *grep_opt = opt->value;
FILE *patterns;
int lno = 0;
struct strbuf sb;
patterns = fopen(arg, "r");
if (!patterns)
die("'%s': %s", arg, strerror(errno));
while (strbuf_getline(&sb, patterns, '\n') == 0) {
/* ignore empty line like grep does */
if (sb.len == 0)
continue;
append_grep_pattern(grep_opt, strbuf_detach(&sb, NULL), arg,
++lno, GREP_PATTERN);
}
fclose(patterns);
strbuf_release(&sb);
return 0;
}
static int not_callback(const struct option *opt, const char *arg, int unset)
{
struct grep_opt *grep_opt = opt->value;
append_grep_pattern(grep_opt, "--not", "command line", 0, GREP_NOT);
return 0;
}
static int and_callback(const struct option *opt, const char *arg, int unset)
{
struct grep_opt *grep_opt = opt->value;
append_grep_pattern(grep_opt, "--and", "command line", 0, GREP_AND);
return 0;
}
static int open_callback(const struct option *opt, const char *arg, int unset)
{
struct grep_opt *grep_opt = opt->value;
append_grep_pattern(grep_opt, "(", "command line", 0, GREP_OPEN_PAREN);
return 0;
}
static int close_callback(const struct option *opt, const char *arg, int unset)
{
struct grep_opt *grep_opt = opt->value;
append_grep_pattern(grep_opt, ")", "command line", 0, GREP_CLOSE_PAREN);
return 0;
}
static int pattern_callback(const struct option *opt, const char *arg,
int unset)
{
struct grep_opt *grep_opt = opt->value;
append_grep_pattern(grep_opt, arg, "-e option", 0, GREP_PATTERN);
return 0;
}
static int help_callback(const struct option *opt, const char *arg, int unset)
{
return -1;
}
int cmd_grep(int argc, const char **argv, const char *prefix) int cmd_grep(int argc, const char **argv, const char *prefix)
{ {
int hit = 0; int hit = 0;
int cached = 0; int cached = 0;
int external_grep_allowed = 1;
int seen_dashdash = 0; int seen_dashdash = 0;
struct grep_opt opt; struct grep_opt opt;
struct object_array list = { 0, 0, NULL }; struct object_array list = { 0, 0, NULL };
const char **paths = NULL; const char **paths = NULL;
int i; int i;
int dummy;
struct option options[] = {
OPT_BOOLEAN(0, "cached", &cached,
"search in index instead of in the work tree"),
OPT_GROUP(""),
OPT_BOOLEAN('v', "invert-match", &opt.invert,
"show non-matching lines"),
OPT_BIT('i', "ignore-case", &opt.regflags,
"case insensitive matching", REG_ICASE),
OPT_BOOLEAN('w', "word-regexp", &opt.word_regexp,
"match patterns only at word boundaries"),
OPT_SET_INT('a', "text", &opt.binary,
"process binary files as text", GREP_BINARY_TEXT),
OPT_SET_INT('I', NULL, &opt.binary,
"don't match patterns in binary files",
GREP_BINARY_NOMATCH),
OPT_GROUP(""),
OPT_BIT('E', "extended-regexp", &opt.regflags,
"use extended POSIX regular expressions", REG_EXTENDED),
OPT_NEGBIT('G', "basic-regexp", &opt.regflags,
"use basic POSIX regular expressions (default)",
REG_EXTENDED),
OPT_BOOLEAN('F', "fixed-strings", &opt.fixed,
"interpret patterns as fixed strings"),
OPT_GROUP(""),
OPT_BOOLEAN('n', NULL, &opt.linenum, "show line numbers"),
OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1),
OPT_BIT('H', NULL, &opt.pathname, "show filenames", 1),
OPT_NEGBIT(0, "full-name", &opt.relative,
"show filenames relative to top directory", 1),
OPT_BOOLEAN('l', "files-with-matches", &opt.name_only,
"show only filenames instead of matching lines"),
OPT_BOOLEAN(0, "name-only", &opt.name_only,
"synonym for --files-with-matches"),
OPT_BOOLEAN('L', "files-without-match",
&opt.unmatch_name_only,
"show only the names of files without match"),
OPT_BOOLEAN('z', "null", &opt.null_following_name,
"print NUL after filenames"),
OPT_BOOLEAN('c', "count", &opt.count,
"show the number of matches instead of matching lines"),
OPT_SET_INT(0, "color", &opt.color, "highlight matches", 1),
OPT_GROUP(""),
OPT_CALLBACK('C', NULL, &opt, "n",
"show <n> context lines before and after matches",
context_callback),
OPT_INTEGER('B', NULL, &opt.pre_context,
"show <n> context lines before matches"),
OPT_INTEGER('A', NULL, &opt.post_context,
"show <n> context lines after matches"),
OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
context_callback),
OPT_GROUP(""),
OPT_CALLBACK('f', NULL, &opt, "file",
"read patterns from file", file_callback),
{ OPTION_CALLBACK, 'e', NULL, &opt, "pattern",
"match <pattern>", PARSE_OPT_NONEG, pattern_callback },
{ OPTION_CALLBACK, 0, "and", &opt, NULL,
"combine patterns specified with -e",
PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback },
OPT_BOOLEAN(0, "or", &dummy, ""),
{ OPTION_CALLBACK, 0, "not", &opt, NULL, "",
PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback },
{ OPTION_CALLBACK, '(', NULL, &opt, NULL, "",
PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
open_callback },
{ OPTION_CALLBACK, ')', NULL, &opt, NULL, "",
PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
close_callback },
OPT_BOOLEAN(0, "all-match", &opt.all_match,
"show only matches from files that match all patterns"),
OPT_GROUP(""),
#if NO_EXTERNAL_GREP
OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed,
"allow calling of grep(1) (ignored by this build)"),
#else
OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed,
"allow calling of grep(1) (default)"),
#endif
{ OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage",
PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
OPT_END()
};
memset(&opt, 0, sizeof(opt)); memset(&opt, 0, sizeof(opt));
opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0; opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
@ -536,6 +749,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
opt.pattern_tail = &opt.pattern_list; opt.pattern_tail = &opt.pattern_list;
opt.regflags = REG_NEWLINE; 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 * If there is no -- then the paths must exist in the working
* tree. If there is no explicit pattern specified with -e or * tree. If there is no explicit pattern specified with -e or
@ -546,217 +765,21 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
* unrecognized non option is the beginning of the refs list * unrecognized non option is the beginning of the refs list
* that continues up to the -- (if exists), and then paths. * that continues up to the -- (if exists), and then paths.
*/ */
argc = parse_options(argc, argv, options, grep_usage,
PARSE_OPT_KEEP_DASHDASH |
PARSE_OPT_STOP_AT_NON_OPTION |
PARSE_OPT_NO_INTERNAL_HELP);
while (1 < argc) { /* First unrecognized non-option token */
const char *arg = argv[1]; if (argc > 0 && !opt.pattern_list) {
argc--; argv++; append_grep_pattern(&opt, argv[0], "command line", 0,
if (!strcmp("--cached", arg)) { GREP_PATTERN);
cached = 1; argv++;
continue; argc--;
}
if (!strcmp("--no-ext-grep", arg)) {
builtin_grep = 1;
continue;
}
if (!strcmp("-a", arg) ||
!strcmp("--text", arg)) {
opt.binary = GREP_BINARY_TEXT;
continue;
}
if (!strcmp("-i", arg) ||
!strcmp("--ignore-case", arg)) {
opt.regflags |= REG_ICASE;
continue;
}
if (!strcmp("-I", arg)) {
opt.binary = GREP_BINARY_NOMATCH;
continue;
}
if (!strcmp("-v", arg) ||
!strcmp("--invert-match", arg)) {
opt.invert = 1;
continue;
}
if (!strcmp("-E", arg) ||
!strcmp("--extended-regexp", arg)) {
opt.regflags |= REG_EXTENDED;
continue;
}
if (!strcmp("-F", arg) ||
!strcmp("--fixed-strings", arg)) {
opt.fixed = 1;
continue;
}
if (!strcmp("-G", arg) ||
!strcmp("--basic-regexp", arg)) {
opt.regflags &= ~REG_EXTENDED;
continue;
}
if (!strcmp("-n", arg)) {
opt.linenum = 1;
continue;
}
if (!strcmp("-h", arg)) {
opt.pathname = 0;
continue;
}
if (!strcmp("-H", arg)) {
opt.pathname = 1;
continue;
}
if (!strcmp("-l", arg) ||
!strcmp("--name-only", arg) ||
!strcmp("--files-with-matches", arg)) {
opt.name_only = 1;
continue;
}
if (!strcmp("-L", arg) ||
!strcmp("--files-without-match", arg)) {
opt.unmatch_name_only = 1;
continue;
}
if (!strcmp("-z", arg) ||
!strcmp("--null", arg)) {
opt.null_following_name = 1;
continue;
}
if (!strcmp("-c", arg) ||
!strcmp("--count", arg)) {
opt.count = 1;
continue;
}
if (!strcmp("-w", arg) ||
!strcmp("--word-regexp", arg)) {
opt.word_regexp = 1;
continue;
}
if (!prefixcmp(arg, "-A") ||
!prefixcmp(arg, "-B") ||
!prefixcmp(arg, "-C") ||
(arg[0] == '-' && '1' <= arg[1] && arg[1] <= '9')) {
unsigned num;
const char *scan;
switch (arg[1]) {
case 'A': case 'B': case 'C':
if (!arg[2]) {
if (argc <= 1)
die(emsg_missing_context_len);
scan = *++argv;
argc--;
}
else
scan = arg + 2;
break;
default:
scan = arg + 1;
break;
}
if (strtoul_ui(scan, 10, &num))
die(emsg_invalid_context_len, scan);
switch (arg[1]) {
case 'A':
opt.post_context = num;
break;
default:
case 'C':
opt.post_context = num;
case 'B':
opt.pre_context = num;
break;
}
continue;
}
if (!strcmp("-f", arg)) {
FILE *patterns;
int lno = 0;
char buf[1024];
if (argc <= 1)
die(emsg_missing_argument, arg);
patterns = fopen(argv[1], "r");
if (!patterns)
die("'%s': %s", argv[1], strerror(errno));
while (fgets(buf, sizeof(buf), patterns)) {
int len = strlen(buf);
if (len && buf[len-1] == '\n')
buf[len-1] = 0;
/* ignore empty line like grep does */
if (!buf[0])
continue;
append_grep_pattern(&opt, xstrdup(buf),
argv[1], ++lno,
GREP_PATTERN);
}
fclose(patterns);
argv++;
argc--;
continue;
}
if (!strcmp("--not", arg)) {
append_grep_pattern(&opt, arg, "command line", 0,
GREP_NOT);
continue;
}
if (!strcmp("--and", arg)) {
append_grep_pattern(&opt, arg, "command line", 0,
GREP_AND);
continue;
}
if (!strcmp("--or", arg))
continue; /* no-op */
if (!strcmp("(", arg)) {
append_grep_pattern(&opt, arg, "command line", 0,
GREP_OPEN_PAREN);
continue;
}
if (!strcmp(")", arg)) {
append_grep_pattern(&opt, arg, "command line", 0,
GREP_CLOSE_PAREN);
continue;
}
if (!strcmp("--all-match", arg)) {
opt.all_match = 1;
continue;
}
if (!strcmp("-e", arg)) {
if (1 < argc) {
append_grep_pattern(&opt, argv[1],
"-e option", 0,
GREP_PATTERN);
argv++;
argc--;
continue;
}
die(emsg_missing_argument, arg);
}
if (!strcmp("--full-name", arg)) {
opt.relative = 0;
continue;
}
if (!strcmp("--", arg)) {
/* later processing wants to have this at argv[1] */
argv--;
argc++;
break;
}
if (*arg == '-')
usage(builtin_grep_usage);
/* First unrecognized non-option token */
if (!opt.pattern_list) {
append_grep_pattern(&opt, arg, "command line", 0,
GREP_PATTERN);
break;
}
else {
/* We are looking at the first path or rev;
* it is found at argv[1] after leaving the
* loop.
*/
argc++; argv--;
break;
}
} }
if (opt.color && !opt.color_external)
external_grep_allowed = 0;
if (!opt.pattern_list) if (!opt.pattern_list)
die("no pattern given."); die("no pattern given.");
if ((opt.regflags != REG_NEWLINE) && opt.fixed) if ((opt.regflags != REG_NEWLINE) && opt.fixed)
@ -764,7 +787,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
compile_grep_patterns(&opt); compile_grep_patterns(&opt);
/* Check revs and then paths */ /* Check revs and then paths */
for (i = 1; i < argc; i++) { for (i = 0; i < argc; i++) {
const char *arg = argv[i]; const char *arg = argv[i];
unsigned char sha1[20]; unsigned char sha1[20];
/* Is it a rev? */ /* Is it a rev? */
@ -807,7 +830,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (!list.nr) { if (!list.nr) {
if (!cached) if (!cached)
setup_work_tree(); setup_work_tree();
return !grep_cache(&opt, paths, cached); return !grep_cache(&opt, paths, cached, external_grep_allowed);
} }
if (cached) if (cached)

Просмотреть файл

@ -114,7 +114,7 @@ static int check_emacsclient_version(void)
return 0; return 0;
} }
static void exec_woman_emacs(const char* path, const char *page) static void exec_woman_emacs(const char *path, const char *page)
{ {
if (!check_emacsclient_version()) { if (!check_emacsclient_version()) {
/* This works only with emacsclient version >= 22. */ /* This works only with emacsclient version >= 22. */
@ -128,7 +128,7 @@ static void exec_woman_emacs(const char* path, const char *page)
} }
} }
static void exec_man_konqueror(const char* path, const char *page) static void exec_man_konqueror(const char *path, const char *page)
{ {
const char *display = getenv("DISPLAY"); const char *display = getenv("DISPLAY");
if (display && *display) { if (display && *display) {
@ -156,7 +156,7 @@ static void exec_man_konqueror(const char* path, const char *page)
} }
} }
static void exec_man_man(const char* path, const char *page) static void exec_man_man(const char *path, const char *page)
{ {
if (!path) if (!path)
path = "man"; path = "man";
@ -236,7 +236,7 @@ static int add_man_viewer_info(const char *var, const char *value)
const char *subkey = strrchr(name, '.'); const char *subkey = strrchr(name, '.');
if (!subkey) if (!subkey)
return error("Config with no key for man viewer: %s", name); return 0;
if (!strcmp(subkey, ".path")) { if (!strcmp(subkey, ".path")) {
if (!value) if (!value)
@ -249,7 +249,6 @@ static int add_man_viewer_info(const char *var, const char *value)
return add_man_viewer_cmd(name, subkey - name, value); return add_man_viewer_cmd(name, subkey - name, value);
} }
warning("'%s': unsupported man viewer sub key.", subkey);
return 0; return 0;
} }

Просмотреть файл

@ -132,8 +132,7 @@ static void copy_templates(const char *template_dir)
} }
dir = opendir(template_path); dir = opendir(template_path);
if (!dir) { if (!dir) {
fprintf(stderr, "warning: templates not found %s\n", warning("templates not found %s", template_dir);
template_dir);
return; return;
} }
@ -146,8 +145,8 @@ static void copy_templates(const char *template_dir)
if (repository_format_version && if (repository_format_version &&
repository_format_version != GIT_REPO_VERSION) { repository_format_version != GIT_REPO_VERSION) {
fprintf(stderr, "warning: not copying templates of " warning("not copying templates of "
"a wrong format version %d from '%s'\n", "a wrong format version %d from '%s'",
repository_format_version, repository_format_version,
template_dir); template_dir);
closedir(dir); closedir(dir);

Просмотреть файл

@ -17,6 +17,7 @@
#include "run-command.h" #include "run-command.h"
#include "shortlog.h" #include "shortlog.h"
#include "remote.h" #include "remote.h"
#include "string-list.h"
/* Set a default date-time format for git log ("log.date" config variable) */ /* Set a default date-time format for git log ("log.date" config variable) */
static const char *default_date_mode = NULL; static const char *default_date_mode = NULL;
@ -416,18 +417,13 @@ int cmd_log(int argc, const char **argv, const char *prefix)
} }
/* format-patch */ /* format-patch */
#define FORMAT_PATCH_NAME_MAX 64
static int istitlechar(char c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') || c == '.' || c == '_';
}
static const char *fmt_patch_suffix = ".patch"; static const char *fmt_patch_suffix = ".patch";
static int numbered = 0; static int numbered = 0;
static int auto_number = 1; static int auto_number = 1;
static char *default_attach = NULL;
static char **extra_hdr; static char **extra_hdr;
static int extra_hdr_nr; static int extra_hdr_nr;
static int extra_hdr_alloc; static int extra_hdr_alloc;
@ -459,6 +455,11 @@ static void add_header(const char *value)
extra_hdr[extra_hdr_nr++] = xstrndup(value, len); extra_hdr[extra_hdr_nr++] = xstrndup(value, len);
} }
#define THREAD_SHALLOW 1
#define THREAD_DEEP 2
static int thread = 0;
static int do_signoff = 0;
static int git_format_config(const char *var, const char *value, void *cb) static int git_format_config(const char *var, const char *value, void *cb)
{ {
if (!strcmp(var, "format.headers")) { if (!strcmp(var, "format.headers")) {
@ -488,94 +489,60 @@ static int git_format_config(const char *var, const char *value, void *cb)
auto_number = auto_number && numbered; auto_number = auto_number && numbered;
return 0; 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;
}
if (!strcmp(var, "format.signoff")) {
do_signoff = git_config_bool(var, value);
return 0;
}
return git_log_config(var, value, cb); return git_log_config(var, value, cb);
} }
static const char *get_oneline_for_filename(struct commit *commit,
int keep_subject)
{
static char filename[PATH_MAX];
char *sol;
int len = 0;
int suffix_len = strlen(fmt_patch_suffix) + 1;
sol = strstr(commit->buffer, "\n\n");
if (!sol)
filename[0] = '\0';
else {
int j, space = 0;
sol += 2;
/* strip [PATCH] or [PATCH blabla] */
if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
char *eos = strchr(sol + 6, ']');
if (eos) {
while (isspace(*eos))
eos++;
sol = eos;
}
}
for (j = 0;
j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
len < sizeof(filename) - suffix_len &&
sol[j] && sol[j] != '\n';
j++) {
if (istitlechar(sol[j])) {
if (space) {
filename[len++] = '-';
space = 0;
}
filename[len++] = sol[j];
if (sol[j] == '.')
while (sol[j + 1] == '.')
j++;
} else
space = 1;
}
while (filename[len - 1] == '.'
|| filename[len - 1] == '-')
len--;
filename[len] = '\0';
}
return filename;
}
static FILE *realstdout = NULL; static FILE *realstdout = NULL;
static const char *output_directory = NULL; static const char *output_directory = NULL;
static int outdir_offset; static int outdir_offset;
static int reopen_stdout(const char *oneline, int nr, int total) static int reopen_stdout(struct commit *commit, struct rev_info *rev)
{ {
char filename[PATH_MAX]; struct strbuf filename = STRBUF_INIT;
int len = 0;
int suffix_len = strlen(fmt_patch_suffix) + 1; int suffix_len = strlen(fmt_patch_suffix) + 1;
if (output_directory) { if (output_directory) {
len = snprintf(filename, sizeof(filename), "%s", strbuf_addstr(&filename, output_directory);
output_directory); if (filename.len >=
if (len >= PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len)
sizeof(filename) - FORMAT_PATCH_NAME_MAX - suffix_len)
return error("name of output directory is too long"); return error("name of output directory is too long");
if (filename[len - 1] != '/') if (filename.buf[filename.len - 1] != '/')
filename[len++] = '/'; strbuf_addch(&filename, '/');
} }
if (!oneline) get_patch_filename(commit, rev->nr, fmt_patch_suffix, &filename);
len += sprintf(filename + len, "%d", nr);
else {
len += sprintf(filename + len, "%04d-", nr);
len += snprintf(filename + len, sizeof(filename) - len - 1
- suffix_len, "%s", oneline);
strcpy(filename + len, fmt_patch_suffix);
}
fprintf(realstdout, "%s\n", filename + outdir_offset); if (!DIFF_OPT_TST(&rev->diffopt, QUIET))
if (freopen(filename, "w", stdout) == NULL) fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
return error("Cannot open patch file %s",filename);
if (freopen(filename.buf, "w", stdout) == NULL)
return error("Cannot open patch file %s", filename.buf);
strbuf_release(&filename);
return 0; return 0;
} }
@ -645,7 +612,6 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
int nr, struct commit **list, struct commit *head) int nr, struct commit **list, struct commit *head)
{ {
const char *committer; const char *committer;
char *head_sha1;
const char *subject_start = NULL; const char *subject_start = NULL;
const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n"; const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
const char *msg; const char *msg;
@ -656,21 +622,41 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
const char *encoding = "utf-8"; const char *encoding = "utf-8";
struct diff_options opts; struct diff_options opts;
int need_8bit_cte = 0; int need_8bit_cte = 0;
struct commit *commit = NULL;
if (rev->commit_format != CMIT_FMT_EMAIL) if (rev->commit_format != CMIT_FMT_EMAIL)
die("Cover letter needs email format"); die("Cover letter needs email format");
if (!use_stdout && reopen_stdout(numbered_files ? committer = git_committer_info(0);
NULL : "cover-letter", 0, rev->total))
if (!numbered_files) {
/*
* We fake a commit for the cover letter so we get the filename
* desired.
*/
commit = xcalloc(1, sizeof(*commit));
commit->buffer = xmalloc(400);
snprintf(commit->buffer, 400,
"tree 0000000000000000000000000000000000000000\n"
"parent %s\n"
"author %s\n"
"committer %s\n\n"
"cover letter\n",
sha1_to_hex(head->object.sha1), committer, committer);
}
if (!use_stdout && reopen_stdout(commit, rev))
return; return;
head_sha1 = sha1_to_hex(head->object.sha1); if (commit) {
log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers, free(commit->buffer);
free(commit);
}
log_write_email_headers(rev, head, &subject_start, &extra_headers,
&need_8bit_cte); &need_8bit_cte);
committer = git_committer_info(0);
msg = body; msg = body;
pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822, pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
encoding); encoding);
@ -766,10 +752,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
int numbered_files = 0; /* _just_ numbers */ int numbered_files = 0; /* _just_ numbers */
int subject_prefix = 0; int subject_prefix = 0;
int ignore_if_in_upstream = 0; int ignore_if_in_upstream = 0;
int thread = 0;
int cover_letter = 0; int cover_letter = 0;
int boundary_count = 0; int boundary_count = 0;
int no_binary_diff = 0; int no_binary_diff = 0;
int numbered_cmdline_opt = 0;
struct commit *origin = NULL, *head = NULL; struct commit *origin = NULL, *head = NULL;
const char *in_reply_to = NULL; const char *in_reply_to = NULL;
struct patch_ids ids; struct patch_ids ids;
@ -787,6 +773,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.subject_prefix = fmt_patch_subject_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 * Parse the arguments before setup_revisions(), or something
* like "git format-patch -o a123 HEAD^.." may fail; a123 is * like "git format-patch -o a123 HEAD^.." may fail; a123 is
@ -796,8 +787,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (!strcmp(argv[i], "--stdout")) if (!strcmp(argv[i], "--stdout"))
use_stdout = 1; use_stdout = 1;
else if (!strcmp(argv[i], "-n") || else if (!strcmp(argv[i], "-n") ||
!strcmp(argv[i], "--numbered")) !strcmp(argv[i], "--numbered")) {
numbered = 1; numbered = 1;
numbered_cmdline_opt = 1;
}
else if (!strcmp(argv[i], "-N") || else if (!strcmp(argv[i], "-N") ||
!strcmp(argv[i], "--no-numbered")) { !strcmp(argv[i], "--no-numbered")) {
numbered = 0; numbered = 0;
@ -833,13 +826,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
} }
else if (!strcmp(argv[i], "--signoff") || else if (!strcmp(argv[i], "--signoff") ||
!strcmp(argv[i], "-s")) { !strcmp(argv[i], "-s")) {
const char *committer; do_signoff = 1;
const char *endpos;
committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
endpos = strchr(committer, '>');
if (!endpos)
die("bogus committer info %s", committer);
add_signoff = xmemdupz(committer, endpos - committer + 1);
} }
else if (!strcmp(argv[i], "--attach")) { else if (!strcmp(argv[i], "--attach")) {
rev.mime_boundary = git_version_string; rev.mime_boundary = git_version_string;
@ -849,6 +836,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.mime_boundary = argv[i] + 9; rev.mime_boundary = argv[i] + 9;
rev.no_inline = 1; 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")) { else if (!strcmp(argv[i], "--inline")) {
rev.mime_boundary = git_version_string; rev.mime_boundary = git_version_string;
rev.no_inline = 0; rev.no_inline = 0;
@ -859,8 +850,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
} }
else if (!strcmp(argv[i], "--ignore-if-in-upstream")) else if (!strcmp(argv[i], "--ignore-if-in-upstream"))
ignore_if_in_upstream = 1; ignore_if_in_upstream = 1;
else if (!strcmp(argv[i], "--thread")) else if (!strcmp(argv[i], "--thread")
thread = 1; || !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=")) else if (!prefixcmp(argv[i], "--in-reply-to="))
in_reply_to = argv[i] + 14; in_reply_to = argv[i] + 14;
else if (!strcmp(argv[i], "--in-reply-to")) { else if (!strcmp(argv[i], "--in-reply-to")) {
@ -877,11 +873,23 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
cover_letter = 1; cover_letter = 1;
else if (!strcmp(argv[i], "--no-binary")) else if (!strcmp(argv[i], "--no-binary"))
no_binary_diff = 1; no_binary_diff = 1;
else if (!prefixcmp(argv[i], "--add-header="))
add_header(argv[i] + 13);
else else
argv[j++] = argv[i]; argv[j++] = argv[i];
} }
argc = j; argc = j;
if (do_signoff) {
const char *committer;
const char *endpos;
committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
endpos = strchr(committer, '>');
if (!endpos)
die("bogus committer info %s", committer);
add_signoff = xmemdupz(committer, endpos - committer + 1);
}
for (i = 0; i < extra_hdr_nr; i++) { for (i = 0; i < extra_hdr_nr; i++) {
strbuf_addstr(&buf, extra_hdr[i]); strbuf_addstr(&buf, extra_hdr[i]);
strbuf_addch(&buf, '\n'); strbuf_addch(&buf, '\n');
@ -913,6 +921,15 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (start_number < 0) if (start_number < 0)
start_number = 1; start_number = 1;
/*
* If numbered is set solely due to format.numbered in config,
* and it would conflict with --keep-subject (-k) from the
* command line, reset "numbered".
*/
if (numbered && keep_subject && !numbered_cmdline_opt)
numbered = 0;
if (numbered && keep_subject) if (numbered && keep_subject)
die ("-n and -k are mutually exclusive."); die ("-n and -k are mutually exclusive.");
if (keep_subject && subject_prefix) if (keep_subject && subject_prefix)
@ -1009,8 +1026,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
numbered = 1; numbered = 1;
if (numbered) if (numbered)
rev.total = total + start_number - 1; rev.total = total + start_number - 1;
if (in_reply_to) if (in_reply_to || thread || cover_letter)
rev.ref_message_id = clean_message_id(in_reply_to); 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);
}
rev.numbered_files = numbered_files;
rev.patch_suffix = fmt_patch_suffix;
if (cover_letter) { if (cover_letter) {
if (thread) if (thread)
gen_message_id(&rev, "cover"); gen_message_id(&rev, "cover");
@ -1029,21 +1052,39 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
/* Have we already had a message ID? */ /* Have we already had a message ID? */
if (rev.message_id) { if (rev.message_id) {
/* /*
* If we've got the ID to be a reply * For deep threading: make every mail
* to, discard the current ID; * a reply to the previous one, no
* otherwise, make everything a reply * matter what other options are set.
* to that. *
* 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); free(rev.message_id);
else 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)); 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), if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit,
rev.nr, rev.total)) &rev))
die("Failed to create output files"); die("Failed to create output files");
shown = log_tree_commit(&rev, commit); shown = log_tree_commit(&rev, commit);
free(commit->buffer); free(commit->buffer);

Просмотреть файл

@ -10,6 +10,7 @@
#include "dir.h" #include "dir.h"
#include "builtin.h" #include "builtin.h"
#include "tree.h" #include "tree.h"
#include "parse-options.h"
static int abbrev; static int abbrev;
static int show_deleted; static int show_deleted;
@ -28,6 +29,7 @@ static const char **pathspec;
static int error_unmatch; static int error_unmatch;
static char *ps_matched; static char *ps_matched;
static const char *with_tree; static const char *with_tree;
static int exc_given;
static const char *tag_cached = ""; static const char *tag_cached = "";
static const char *tag_unmerged = ""; 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++) { for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i]; struct cache_entry *ce = active_cache[i];
int dtype = ce_to_dtype(ce); 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; continue;
if (show_unmerged && !ce_stage(ce)) if (show_unmerged && !ce_stage(ce))
continue; continue;
@ -189,7 +192,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)
struct stat st; struct stat st;
int err; int err;
int dtype = ce_to_dtype(ce); 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; continue;
if (ce->ce_flags & CE_UPDATE) if (ce->ce_flags & CE_UPDATE)
continue; continue;
@ -374,159 +378,141 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_
return errors; return errors;
} }
static const char ls_files_usage[] = static const char * const ls_files_usage[] = {
"git ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* " "git ls-files [options] [<file>]*",
"[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] " NULL
"[ --exclude-per-directory=<filename> ] [--exclude-standard] " };
"[--full-name] [--abbrev] [--] [<file>]*";
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 cmd_ls_files(int argc, const char **argv, const char *prefix)
{ {
int i; int require_work_tree = 0, show_tag = 0;
int exc_given = 0, require_work_tree = 0;
struct dir_struct dir; 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_NEGBIT(0, "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)); memset(&dir, 0, sizeof(dir));
if (prefix) if (prefix)
prefix_offset = strlen(prefix); prefix_offset = strlen(prefix);
git_config(git_default_config, NULL); git_config(git_default_config, NULL);
for (i = 1; i < argc; i++) { argc = parse_options(argc, argv, builtin_ls_files_options,
const char *arg = argv[i]; ls_files_usage, 0);
if (show_tag || show_valid_bit) {
if (!strcmp(arg, "--")) { tag_cached = "H ";
i++; tag_unmerged = "M ";
break; tag_removed = "R ";
} tag_modified = "C ";
if (!strcmp(arg, "-z")) { tag_other = "? ";
line_terminator = 0; tag_killed = "K ";
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;
} }
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()) if (require_work_tree && !is_inside_work_tree())
setup_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 */ /* be nice with submodule paths ending in a slash */
read_cache(); read_cache();
if (pathspec) if (pathspec)
strip_trailing_slash_from_submodules(); strip_trailing_slash_from_submodules();
@ -543,7 +529,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
ps_matched = xcalloc(1, num); 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", fprintf(stderr, "%s: --ignored needs some exclude pattern\n",
argv[0]); argv[0]);
exit(1); exit(1);

Просмотреть файл

@ -60,7 +60,6 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
{ {
int retval = 0; int retval = 0;
const char *type = blob_type; const char *type = blob_type;
unsigned long size;
if (S_ISGITLINK(mode)) { 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_NAME_ONLY)) {
if (ls_options & LS_SHOW_SIZE) { if (ls_options & LS_SHOW_SIZE) {
char size_text[24];
if (!strcmp(type, blob_type)) { if (!strcmp(type, blob_type)) {
sha1_object_info(sha1, &size); unsigned long size;
printf("%06o %s %s %7lu\t", mode, type, if (sha1_object_info(sha1, &size) == OBJ_BAD)
abbrev ? find_unique_abbrev(sha1, abbrev) strcpy(size_text, "BAD");
: sha1_to_hex(sha1), else
size); snprintf(size_text, sizeof(size_text),
"%lu", size);
} else } else
printf("%06o %s %s %7c\t", mode, type, strcpy(size_text, "-");
abbrev ? find_unique_abbrev(sha1, abbrev) printf("%06o %s %s %7s\t", mode, type,
: sha1_to_hex(sha1), abbrev ? find_unique_abbrev(sha1, abbrev)
'-'); : sha1_to_hex(sha1),
size_text);
} else } else
printf("%06o %s %s\t", mode, type, printf("%06o %s %s\t", mode, type,
abbrev ? find_unique_abbrev(sha1, abbrev) abbrev ? find_unique_abbrev(sha1, abbrev)

Просмотреть файл

@ -537,7 +537,6 @@ static int decode_header_bq(struct strbuf *it)
*/ */
strbuf_add(&outbuf, in, ep - in); strbuf_add(&outbuf, in, ep - in);
} }
in = ep;
} }
/* E.g. /* E.g.
* ep : "=?iso-2022-jp?B?GyR...?= foo" * ep : "=?iso-2022-jp?B?GyR...?= foo"

Просмотреть файл

@ -45,8 +45,9 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
bases[bases_count++] = sha; bases[bases_count++] = sha;
} }
else else
warning("Cannot handle more than %zu bases. " warning("Cannot handle more than %d bases. "
"Ignoring %s.", ARRAY_SIZE(bases)-1, argv[i]); "Ignoring %s.",
(int)ARRAY_SIZE(bases)-1, argv[i]);
} }
if (argc - i != 3) /* "--" "<head>" "<remote>" */ if (argc - i != 3) /* "--" "<head>" "<remote>" */
die("Not handling anything other than two heads merge."); die("Not handling anything other than two heads merge.");

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше