Merge commit 'v1.5.3-rc2'

This commit is contained in:
Johannes Sixt
2007-07-22 21:09:48 +02:00
308 changed files with 9501 additions and 4139 deletions

2
.gitignore vendored
View File

@@ -96,6 +96,7 @@ git-push
git-quiltimport
git-read-tree
git-rebase
git-rebase--interactive
git-receive-pack
git-reflog
git-relink
@@ -123,6 +124,7 @@ git-ssh-fetch
git-ssh-pull
git-ssh-push
git-ssh-upload
git-stash
git-status
git-stripspace
git-submodule

View File

@@ -19,13 +19,16 @@ Horst H. von Brand <vonbrand@inf.utfsm.cl>
Joachim Berdal Haga <cjhaga@fys.uio.no>
Jon Loeliger <jdl@freescale.com>
Jon Seymour <jon@blackcubes.dyndns.org>
Junio C Hamano <junio@twinsun.com>
Karl Hasselström <kha@treskal.com>
Kent Engstrom <kent@lysator.liu.se>
Lars Doelle <lars.doelle@on-line ! de>
Lars Doelle <lars.doelle@on-line.de>
Lukas Sandström <lukass@etek.chalmers.se>
Martin Langhoff <martin@catalyst.net.nz>
Michael Coleman <tutufan@gmail.com>
Michele Ballabio <barra_cuda@katamail.com>
Nanako Shiraishi <nanako3@bluebottle.com>
Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Ramsay Allan Jones <ramsay@ramsay1.demon.co.uk>
René Scharfe <rene.scharfe@lsrfire.ath.cx>
@@ -41,6 +44,7 @@ Uwe Kleine-König <ukleinek@informatik.uni-freiburg.de>
Uwe Kleine-König <uzeisberger@io.fsforth.de>
Uwe Kleine-König <zeisberg@informatik.uni-freiburg.de>
Ville Skyttä <scop@xemacs.org>
William Pursell <bill.pursell@gmail.com>
YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
anonymous <linux@horizon.com>
anonymous <linux@horizon.net>

View File

@@ -41,6 +41,7 @@ ifdef ASCIIDOC8
ASCIIDOC_EXTRA += -a asciidoc7compatible
endif
INSTALL?=install
RM ?= rm -f
DOC_REF = origin/man
-include ../config.mak.autogen
@@ -84,7 +85,7 @@ install: man
# Determine "include::" file references in asciidoc files.
#
doc.dep : $(wildcard *.txt) build-docdep.perl
rm -f $@+ $@
$(RM) $@+ $@
perl ./build-docdep.perl >$@+
mv $@+ $@
@@ -109,11 +110,11 @@ cmd-list.made: cmd-list.perl $(MAN1_TXT)
git.7 git.html: git.txt core-intro.txt
clean:
rm -f *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 howto-index.txt howto/*.html doc.dep
rm -f $(cmds_txt) *.made
$(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 howto-index.txt howto/*.html doc.dep
$(RM) $(cmds_txt) *.made
%.html : %.txt
rm -f $@+ $@
$(RM) $@+ $@
$(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf \
$(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $<
mv $@+ $@
@@ -122,7 +123,7 @@ clean:
xmlto -m callouts.xsl man $<
%.xml : %.txt
rm -f $@+ $@
$(RM) $@+ $@
$(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \
$(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $<
mv $@+ $@
@@ -137,7 +138,7 @@ user-manual.html: user-manual.xml
xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
rm -f $@+ $@
$(RM) $@+ $@
sh ./howto-index.sh $(wildcard howto/*.txt) >$@+
mv $@+ $@
@@ -147,7 +148,7 @@ $(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt
WEBDOC_DEST = /pub/software/scm/git/docs
$(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
rm -f $@+ $@
$(RM) $@+ $@
sed -e '1,/^$$/d' $< | $(ASCIIDOC) -b xhtml11 - >$@+
mv $@+ $@

View File

@@ -4,14 +4,16 @@ GIT v1.5.3 Release Notes
Updates since v1.5.2
--------------------
* An initial interation of Porcelain level superproject support
started to take shape.
* The commit walkers other than http are officially deprecated,
but still supported for now.
* Thee are a handful pack-objects changes to help you cope better with
repositories with pathologically large blobs in them.
* The submodule support has Porcelain layer.
* There are a handful pack-objects changes to help you cope better
with repositories with pathologically large blobs in them.
* For people who need to import from Perforce, a front-end for
fast-import is in contrib/fast-import/ now.
fast-import is in contrib/fast-import/.
* Comes with git-gui 0.8.0.
@@ -19,10 +21,30 @@ Updates since v1.5.2
* New commands and options.
- "git log" learned a new option '--follow', to follow
- "git log --date=<format>" can use more formats: iso8601, rfc2822.
- The hunk header output from "git diff" family can be customized
with the attributes mechanism. See gitattributes(5) for details.
- "git stash" allows you to quickly save away your work in
progress and replay it later on an updated state.
- "git rebase" learned an "interactive" mode that let you
pick and reorder which commits to rebuild.
- "git fsck" can save its findings in $GIT_DIR/lost-found,
without a separate invocation of "git lost-found" command.
- $GIT_WORK_TREE environment variable can be used together with
$GIT_DIR to work in a subdirectory of a working tree that is
not located at "$GIT_DIR/..".
- "git log" learned a new option "--follow", to follow
renaming history of a single file.
- "git-filter-branch" is a reborn cg-admin-rewritehist.
- "git-filter-branch" lets you rewrite the revision history of
the current branch, creating a new branch. You can specify a
number of filters to modify the commits, files and trees.
- "git-cvsserver" learned new options (--base-path, --export-all,
--strict-paths) inspired by git-daemon.
@@ -48,6 +70,10 @@ Updates since v1.5.2
- "git gc --aggressive" tells the command to spend more cycles
to optimize the repository harder.
- "git repack" learned a "window-memory" limit which
dynamically reduces the window size to stay within the
specified memory usage.
- "git repack" can be told to split resulting packs to avoid
exceeding limit specified with "--max-pack-size".
@@ -58,11 +84,15 @@ Updates since v1.5.2
- "git format-patch" learned --numbered-files option. This
may be useful for MH users.
- "git format-patch" learned format.subjectprefix configuration
variable, which serves the same purpose as "--subject-prefix"
option.
- "git tag -n -l" shows tag annotations while listing tags.
- "git cvsimport" can optionally use the separate-remote layout.
- "git blame" can be told to see through commits that changes
- "git blame" can be told to see through commits that change
whitespaces and indentation levels with "-w" option.
- "git send-email" can be told not to thread the messages when
@@ -71,8 +101,21 @@ Updates since v1.5.2
- "git config" learned NUL terminated output format via -z to
help scripts.
- "git init -q" makes the command quieter.
* Updated behavior of existing commands.
- "git rm --cached" does not complain when removing a newly
added file from the index anymore.
- "git svn dcommit" retains local merge information.
- "git config" to set values also honors type flags like --bool
and --int.
- core.quotepath configuration can be used to make textual git
output to emit most of the characters in the path literally.
- "git mergetool" chooses its backend more wisely, taking
notice of its environment such as use of X, Gnome/KDE, etc.
@@ -84,7 +127,7 @@ Updates since v1.5.2
$path/$project/.git are more useful. We use $project part
in the filename, which we used to discard.
- "git cvsimort" creates lightweight tag; there is not any
- "git cvsimport" creates lightweight tags; there is no
interesting information we can record in an annotated tag,
and the handcrafted ones the old code created was not
properly formed anyway.
@@ -99,9 +142,9 @@ Updates since v1.5.2
- "git-apply --whitespace=strip" removes blank lines added at
the end of the file.
- "git-fetch" over git native protocols with -v shows connection
status, and the IP address of the other end, to help
diagnosing problems.
- "git-fetch" over git native protocols with "-v" option shows
connection status, and the IP address of the other end, to
help diagnosing problems.
- We used to have core.legacyheaders configuration, when
set to false, allowed git to write loose objects in a format
@@ -124,8 +167,8 @@ Updates since v1.5.2
.gitattributes. It does not attempt to deltify blobs that
come from paths with delta attribute set to false.
- new-workdir script (in contrib) can now be used with a bare
repository.
- "new-workdir" script (in contrib) can now be used with a
bare repository.
- "git-mergetool" learned to use gvimdiff.
@@ -141,6 +184,11 @@ Updates since v1.5.2
concatenate them into a single line and treat the result as
"oneline".
- "git p4import" has been demoted to contrib status. For
a superior option, checkout the git-p4 front end to
git-fast-import (also in contrib). The man page and p4
rpm have been removed as well.
* Builds
- old-style function definitions (most notably, a function
@@ -153,6 +201,11 @@ Updates since v1.5.2
small enough delta results it creates while looking for the
best delta candidates.
- git-pack-objects learned a new heuristcs to prefer delta
that is shallower in depth over the smallest delta
possible. This improves both overall packfile access
performance and packfile density.
- diff-delta code that is used for packing has been improved
to work better on big files.
@@ -179,6 +232,6 @@ this release, unless otherwise noted.
--
exec >/var/tmp/1
O=v1.5.2.2-603-g7c85173
O=v1.5.3-rc2
echo O=`git describe refs/heads/master`
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint

View File

@@ -105,6 +105,7 @@ git-diff-tree plumbinginterrogators
git-fast-import ancillarymanipulators
git-fetch mainporcelain
git-fetch-pack synchingrepositories
git-filter-branch ancillarymanipulators
git-fmt-merge-msg purehelpers
git-for-each-ref plumbinginterrogators
git-format-patch mainporcelain
@@ -178,6 +179,7 @@ git-show-ref plumbinginterrogators
git-sh-setup purehelpers
git-ssh-fetch synchingrepositories
git-ssh-upload synchingrepositories
git-stash mainporcelain
git-status mainporcelain
git-stripspace purehelpers
git-submodule mainporcelain

View File

@@ -117,6 +117,18 @@ core.fileMode::
the working copy are ignored; useful on broken filesystems like FAT.
See gitlink:git-update-index[1]. True by default.
core.quotepath::
The commands that output paths (e.g. `ls-files`,
`diff`), when not given the `-z` option, will quote
"unusual" characters in the pathname by enclosing the
pathname in a double-quote pair and with backslashes the
same way strings in C source code are quoted. If this
variable is set to false, the bytes higher than 0x80 are
not quoted but output as verbatim. Note that double
quote, backslash and control characters are always
quoted without `-z` regardless of the setting of this
variable.
core.autocrlf::
If true, makes git convert `CRLF` at the end of lines in text files to
`LF` when reading from the filesystem, and convert in reverse when
@@ -269,6 +281,10 @@ core.excludesfile::
of files which are not meant to be tracked. See
gitlink:gitignore[5].
core.pager::
The command that git will use to paginate output. Can be overridden
with the `GIT_PAGER` environment variable.
alias.*::
Command aliases for the gitlink:git[1] command wrapper - e.g.
after defining "alias.last = cat-file commit HEAD", the invocation
@@ -432,6 +448,11 @@ gc.rerereunresolved::
kept for this many days when `git rerere gc` is run.
The default is 15 days. See gitlink:git-rerere[1].
rerere.enabled::
Activate recording of resolved conflicts, so that identical
conflict hunks can be resolved automatically, should they
be encountered again. See gitlink:git-rerere[1].
gitcvs.enabled::
Whether the cvs server interface is enabled for this repository.
See gitlink:git-cvsserver[1].
@@ -546,6 +567,7 @@ merge.verbosity::
message if conflicts were detected. Level 1 outputs only
conflicts, 2 outputs conflicts and file changes. Level 5 and
above outputs debugging information. The default is level 2.
Can be overriden by 'GIT_MERGE_VERBOSITY' environment variable.
merge.<driver>.name::
Defines a human readable name for a custom low-level
@@ -568,6 +590,12 @@ pack.depth::
The maximum delta depth used by gitlink:git-pack-objects[1] when no
maximum depth is given on the command line. Defaults to 50.
pack.windowMemory::
The window memory size limit used by gitlink:git-pack-objects[1]
when no limit is given on the command line. The value can be
suffixed with "k", "m", or "g". Defaults to 0, meaning no
limit.
pack.compression::
An integer -1..9, indicating the compression level for objects
in a pack file. -1 is the zlib default. 0 means no

View File

@@ -528,7 +528,7 @@ paths that have been trivially merged.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sadly, many merges aren't trivial. If there are files that have
been added.moved or removed, or if both branches have modified the
been added, moved or removed, or if both branches have modified the
same file, you will be left with an index tree that contains "merge
entries" in it. Such an index tree can 'NOT' be written out to a tree
object, and you will have to resolve any such merge clashes using

View File

@@ -126,6 +126,13 @@ the file that rename/copy produces, respectively.
If there is need for such substitution then the whole
pathname is put in double quotes.
The similarity index is the percentage of unchanged lines, and
the dissimilarity index is the percentage of changed lines. It
is a rounded down integer, followed by a percent sign. The
similarity index value of 100% is thus reserved for two equal
files, while 100% dissimilarity means that no line from the old
file made it into the new one.
combined diff format
--------------------

View File

@@ -168,5 +168,13 @@
--quiet::
Disable all output of the program. Implies --exit-code.
--ext-diff::
Allow an external diff helper to be executed. If you set an
external diff driver with gitlink:gitattributes(5), you need
to use this option with gitlink:git-log(1) and friends.
--no-ext-diff::
Disallow external diff drivers.
For more detailed explanation on these common options, see also
link:diffcore.html[diffcore documentation].

View File

@@ -41,7 +41,7 @@ to happen.
With a `-d` or `-D` option, `<branchname>` will be deleted. You may
specify more than one branch for deletion. If the branch currently
has a ref log then the ref log will also be deleted. Use -r together with -d
has a reflog then the reflog will also be deleted. Use -r together with -d
to delete remote-tracking branches.
@@ -54,9 +54,9 @@ OPTIONS
Delete a branch irrespective of its index status.
-l::
Create the branch's ref log. This activates recording of
all changes to made the branch ref, enabling use of date
based sha1 expressions such as "<branchname>@{yesterday}".
Create the branch's reflog. This activates recording of
all changes made to the branch ref, enabling use of date
based sha1 expressions such as "<branchname>@\{yesterday}".
-f::
Force the creation of a new branch even if it means deleting

View File

@@ -62,9 +62,9 @@ OPTIONS
configuration variable.
-l::
Create the new branch's ref log. This activates recording of
all changes to made the branch ref, enabling use of date
based sha1 expressions such as "<branchname>@{yesterday}".
Create the new branch's reflog. This activates recording of
all changes made to the branch ref, enabling use of date
based sha1 expressions such as "<branchname>@\{yesterday}".
-m::
If you have local modifications to one or more files that

View File

@@ -64,6 +64,7 @@ OPTIONS
Operate quietly. This flag is passed to "rsync" and
"git-fetch-pack" commands when given.
--no-checkout::
-n::
No checkout of HEAD is performed after the clone is complete.
@@ -106,8 +107,9 @@ OPTIONS
as patches.
<repository>::
The (possibly remote) repository to clone from. It can
be any URL git-fetch supports.
The (possibly remote) repository to clone from. See the
<<URLS,URLS>> section below for more information on specifying
repositories.
<directory>::
The name of a new directory to clone into. The "humanish"
@@ -116,6 +118,8 @@ OPTIONS
for "host.xz:foo/.git"). Cloning into an existing directory
is not allowed.
include::urls.txt[]
Examples
--------

View File

@@ -72,7 +72,7 @@ GIT_AUTHOR_EMAIL:
name = "Your Name"
email = "your@email.address.xz"
A commit comment is read from stdin (max 999 chars). If a changelog
A commit comment is read from stdin. If a changelog
entry is not provided via "<" redirection, "git-commit-tree" will just wait
for one to be entered and terminated with ^D.

View File

@@ -8,7 +8,7 @@ git-commit - Record changes to the repository
SYNOPSIS
--------
[verse]
'git-commit' [-a | --interactive] [-s] [-v]
'git-commit' [-a | --interactive] [-s] [-v] [-u]
[(-c | -C) <commit> | -F <file> | -m <msg> | --amend]
[--no-verify] [-e] [--author <author>]
[--] [[-i | -o ]<file>...]
@@ -71,7 +71,7 @@ OPTIONS
Override the author name used in the commit. Use
`A U Thor <author@example.com>` format.
-m <msg>::
-m <msg>|--message=<msg>::
Use the given <msg> as the commit message.
-s|--signoff::
@@ -115,6 +115,19 @@ but can be used to amend a merge commit.
as well. This is usually not what you want unless you
are concluding a conflicted merge.
-u|--untracked-files::
Show all untracked files, also those in uninteresting
directories, in the "Untracked files:" section of commit
message template. Without this option only its name and
a trailing slash are displayed for each untracked
directory.
-v|--verbose::
Show unified diff between the HEAD commit and what
would be committed at the bottom of the commit message
template. Note that this diff output doesn't have its
lines prefixed with '#'.
-q|--quiet::
Suppress commit summary message.

View File

@@ -302,7 +302,7 @@ change to the project.
data
('from' SP <committish> LF)?
('merge' SP <committish> LF)?
(filemodify | filedelete | filedeleteall)*
(filemodify | filedelete | filecopy | filerename | filedeleteall)*
LF
....
@@ -325,11 +325,13 @@ commit message use a 0 length data. Commit messages are free-form
and are not interpreted by Git. Currently they must be encoded in
UTF-8, as fast-import does not permit other encodings to be specified.
Zero or more `filemodify`, `filedelete` and `filedeleteall` commands
Zero or more `filemodify`, `filedelete`, `filecopy`, `filerename`
and `filedeleteall` commands
may be included to update the contents of the branch prior to
creating the commit. These commands may be supplied in any order.
However it is recommended that a `filedeleteall` command preceed
all `filemodify` commands in the same commit, as `filedeleteall`
all `filemodify`, `filecopy` and `filerename` commands in the same
commit, as `filedeleteall`
wipes the branch clean (see below).
`author`
@@ -495,6 +497,56 @@ here `<path>` is the complete path of the file or subdirectory to
be removed from the branch.
See `filemodify` above for a detailed description of `<path>`.
`filecopy`
^^^^^^^^^^^^
Recursively copies an existing file or subdirectory to a different
location within the branch. The existing file or directory must
exist. If the destination exists it will be completely replaced
by the content copied from the source.
....
'C' SP <path> SP <path> LF
....
here the first `<path>` is the source location and the second
`<path>` is the destination. See `filemodify` above for a detailed
description of what `<path>` may look like. To use a source path
that contains SP the path must be quoted.
A `filecopy` command takes effect immediately. Once the source
location has been copied to the destination any future commands
applied to the source location will not impact the destination of
the copy.
`filerename`
^^^^^^^^^^^^
Renames an existing file or subdirectory to a different location
within the branch. The existing file or directory must exist. If
the destination exists it will be replaced by the source directory.
....
'R' SP <path> SP <path> LF
....
here the first `<path>` is the source location and the second
`<path>` is the destination. See `filemodify` above for a detailed
description of what `<path>` may look like. To use a source path
that contains SP the path must be quoted.
A `filerename` command takes effect immediately. Once the source
location has been renamed to the destination any future commands
applied to the source location will create new files there and not
impact the destination of the rename.
Note that a `filerename` is the same as a `filecopy` followed by a
`filedelete` of the source location. There is a slight performance
advantage to using `filerename`, but the advantage is so small
that it is never worth trying to convert a delete/add pair in
source material into a rename for fast-import. This `filerename`
command is provided just to simplify frontends that already have
rename information and don't want bother with decomposing it into a
`filecopy` followed by a `filedelete`.
`filedeleteall`
^^^^^^^^^^^^^^^
Included in a `commit` command to remove all files (and also all

View File

@@ -35,7 +35,7 @@ include::fetch-options.txt[]
include::pull-fetch-param.txt[]
include::urls.txt[]
include::urls-remotes.txt[]
SEE ALSO
--------

View File

@@ -0,0 +1,273 @@
git-filter-branch(1)
====================
NAME
----
git-filter-branch - Rewrite branches
SYNOPSIS
--------
[verse]
'git-filter-branch' [--env-filter <command>] [--tree-filter <command>]
[--index-filter <command>] [--parent-filter <command>]
[--msg-filter <command>] [--commit-filter <command>]
[--tag-name-filter <command>] [--subdirectory-filter <directory>]
[-d <directory>] <new-branch-name> [<rev-list options>...]
DESCRIPTION
-----------
Lets you rewrite git revision history by creating a new branch from
your current branch, applying custom filters on each revision.
Those filters can modify each tree (e.g. removing a file or running
a perl rewrite on all files) or information about each commit.
Otherwise, all information (including original commit times or merge
information) will be preserved.
The command takes the new branch name as a mandatory argument and
the filters as optional arguments. If you specify no filters, the
commits will be recommitted without any changes, which would normally
have no effect and result in the new branch pointing to the same
branch as your current branch. Nevertheless, this may be useful in
the future for compensating for some git bugs or such, therefore
such a usage is permitted.
*WARNING*! The rewritten history will have different object names for all
the objects and will not converge with the original branch. You will not
be able to easily push and distribute the rewritten branch on top of the
original branch. Please do not use this command if you do not know the
full implications, and avoid using it anyway, if a simple single commit
would suffice to fix your problem.
Always verify that the rewritten version is correct before disposing
the original branch.
Note that since this operation is extensively I/O expensive, it might
be a good idea to redirect the temporary directory off-disk, e.g. on
tmpfs. Reportedly the speedup is very noticeable.
Filters
~~~~~~~
The filters are applied in the order as listed below. The <command>
argument is always evaluated in shell using the 'eval' command (with the
notable exception of the commit filter, for technical reasons).
Prior to that, the $GIT_COMMIT environment variable will be set to contain
the id of the commit being rewritten. Also, GIT_AUTHOR_NAME,
GIT_AUTHOR_EMAIL, GIT_AUTHOR_DATE, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL,
and GIT_COMMITTER_DATE are set according to the current commit.
A 'map' function is available that takes an "original sha1 id" argument
and outputs a "rewritten sha1 id" if the commit has been already
rewritten, and "original sha1 id" otherwise; the 'map' function can
return several ids on separate lines if your commit filter emitted
multiple commits.
OPTIONS
-------
--env-filter <command>::
This is the filter for modifying the environment in which
the commit will be performed. Specifically, you might want
to rewrite the author/committer name/email/time environment
variables (see gitlink:git-commit[1] for details). Do not forget
to re-export the variables.
--tree-filter <command>::
This is the filter for rewriting the tree and its contents.
The argument is evaluated in shell with the working
directory set to the root of the checked out tree. The new tree
is then used as-is (new files are auto-added, disappeared files
are auto-removed - neither .gitignore files nor any other ignore
rules *HAVE ANY EFFECT*!).
--index-filter <command>::
This is the filter for rewriting the index. It is similar to the
tree filter but does not check out the tree, which makes it much
faster. For hairy cases, see gitlink:git-update-index[1].
--parent-filter <command>::
This is the filter for rewriting the commit's parent list.
It will receive the parent string on stdin and shall output
the new parent string on stdout. The parent string is in
a format accepted by gitlink:git-commit-tree[1]: empty for
the initial commit, "-p parent" for a normal commit and
"-p parent1 -p parent2 -p parent3 ..." for a merge commit.
--msg-filter <command>::
This is the filter for rewriting the commit messages.
The argument is evaluated in the shell with the original
commit message on standard input; its standard output is
used as the new commit message.
--commit-filter <command>::
This is the filter for performing the commit.
If this filter is specified, it will be called instead of the
gitlink:git-commit-tree[1] command, with arguments of the form
"<TREE_ID> [-p <PARENT_COMMIT_ID>]..." and the log message on
stdin. The commit id is expected on stdout.
+
As a special extension, the commit filter may emit multiple
commit ids; in that case, ancestors of the original commit will
have all of them as parents.
--tag-name-filter <command>::
This is the filter for rewriting tag names. When passed,
it will be called for every tag ref that points to a rewritten
object (or to a tag object which points to a rewritten object).
The original tag name is passed via standard input, and the new
tag name is expected on standard output.
+
The original tags are not deleted, but can be overwritten;
use "--tag-name-filter=cat" to simply update the tags. In this
case, be very careful and make sure you have the old tags
backed up in case the conversion has run afoul.
+
Note that there is currently no support for proper rewriting of
tag objects; in layman terms, if the tag has a message or signature
attached, the rewritten tag won't have it. Sorry. (It is by
definition impossible to preserve signatures at any rate.)
--subdirectory-filter <directory>::
Only look at the history which touches the given subdirectory.
The result will contain that directory (and only that) as its
project root.
-d <directory>::
Use this option to set the path to the temporary directory used for
rewriting. When applying a tree filter, the command needs to
temporary checkout the tree to some directory, which may consume
considerable space in case of large projects. By default it
does this in the '.git-rewrite/' directory but you can override
that choice by this parameter.
<rev-list-options>::
When options are given after the new branch name, they will
be passed to gitlink:git-rev-list[1]. Only commits in the resulting
output will be filtered, although the filtered commits can still
reference parents which are outside of that set.
Examples
--------
Suppose you want to remove a file (containing confidential information
or copyright violation) from all commits:
-------------------------------------------------------
git filter-branch --tree-filter 'rm filename' newbranch
-------------------------------------------------------
A significantly faster version:
-------------------------------------------------------------------------------
git filter-branch --index-filter 'git update-index --remove filename' newbranch
-------------------------------------------------------------------------------
Now, you will get the rewritten history saved in the branch 'newbranch'
(your current branch is left untouched).
To set a commit (which typically is at the tip of another
history) to be the parent of the current initial commit, in
order to paste the other history behind the current history:
------------------------------------------------------------------------
git filter-branch --parent-filter 'sed "s/^\$/-p <graft-id>/"' newbranch
------------------------------------------------------------------------
(if the parent string is empty - therefore we are dealing with the
initial commit - add graftcommit as a parent). Note that this assumes
history with a single root (that is, no merge without common ancestors
happened). If this is not the case, use:
-------------------------------------------------------------------------------
git filter-branch --parent-filter \
'cat; test $GIT_COMMIT = <commit-id> && echo "-p <graft-id>"' newbranch
-------------------------------------------------------------------------------
or even simpler:
-----------------------------------------------
echo "$commit-id $graft-id" >> .git/info/grafts
git filter-branch newbranch $graft-id..
-----------------------------------------------
To remove commits authored by "Darl McBribe" from the history:
------------------------------------------------------------------------------
git filter-branch --commit-filter '
if [ "$GIT_AUTHOR_NAME" = "Darl McBribe" ];
then
shift;
while [ -n "$1" ];
do
shift;
echo "$1";
shift;
done;
else
git commit-tree "$@";
fi' newbranch
------------------------------------------------------------------------------
The shift magic first throws away the tree id and then the -p
parameters. Note that this handles merges properly! In case Darl
committed a merge between P1 and P2, it will be propagated properly
and all children of the merge will become merge commits with P1,P2
as their parents instead of the merge commit.
To restrict rewriting to only part of the history, specify a revision
range in addition to the new branch name. The new branch name will
point to the top-most revision that a 'git rev-list' of this range
will print.
Note that the changes introduced by the commits, and not reverted by
subsequent commits, will still be in the rewritten branch. If you want
to throw out _changes_ together with the commits, you should use the
interactive mode of gitlink:git-rebase[1].
Consider this history:
------------------
D--E--F--G--H
/ /
A--B-----C
------------------
To rewrite only commits D,E,F,G,H, but leave A, B and C alone, use:
--------------------------------
git filter-branch ... new-H C..H
--------------------------------
To rewrite commits E,F,G,H, use one of these:
----------------------------------------
git filter-branch ... new-H C..H --not D
git filter-branch ... new-H D..H --not C
----------------------------------------
To move the whole tree into a subdirectory, or remove it from there:
---------------------------------------------------------------
git filter-branch --index-filter \
'git ls-files -s | sed "s-\t-&newsubdir/-" |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
git update-index --index-info &&
mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' directorymoved
---------------------------------------------------------------
Author
------
Written by Petr "Pasky" Baudis <pasky@suse.cz>,
and the git list <git@vger.kernel.org>
Documentation
--------------
Documentation by Petr Baudis and the git list.
GIT
---
Part of the gitlink:git[7] suite

View File

@@ -129,12 +129,13 @@ not add any suffix.
CONFIGURATION
-------------
You can specify extra mail header lines to be added to each
message in the repository configuration. Also you can specify
the default suffix different from the built-in one:
message in the repository configuration. You can also specify
new defaults for the subject prefix and file suffix.
------------
[format]
headers = "Organization: git-foo\n"
subjectprefix = CHANGE
suffix = .txt
------------

View File

@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git-fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]
[--full] [--strict] [--verbose] [<object>*]
[--full] [--strict] [--verbose] [--lost-found] [<object>*]
DESCRIPTION
-----------
@@ -64,6 +64,10 @@ index file and all SHA1 references in .git/refs/* as heads.
--verbose::
Be chatty.
--lost-found::
Write dangling refs into .git/lost-found/commit/ or
.git/lost-found/other/, depending on type.
It tests SHA1 and general object sanity, and it does full tracking of
the resulting reachability and everything else. It prints out any
corruption it finds (missing or bad objects), and if you use the

View File

@@ -8,7 +8,7 @@ git-init-db - Creates an empty git repository
SYNOPSIS
--------
'git-init-db' [--template=<template_directory>] [--shared[=<permissions>]]
'git-init-db' [-q | --quiet] [--template=<template_directory>] [--shared[=<permissions>]]
DESCRIPTION

View File

@@ -8,7 +8,7 @@ git-init - Create an empty git repository or reinitialize an existing one
SYNOPSIS
--------
'git-init' [--template=<template_directory>] [--shared[=<permissions>]]
'git-init' [-q | --quiet] [--template=<template_directory>] [--shared[=<permissions>]]
OPTIONS
@@ -16,6 +16,10 @@ OPTIONS
--
-q, \--quiet::
Only print error and warning messages, all other output will be suppressed.
--template=<template_directory>::
Provide the directory from which templates will be used. The default template

View File

@@ -14,6 +14,8 @@ SYNOPSIS
DESCRIPTION
-----------
THIS COMMAND IS DEPRECATED.
Duplicates another git repository on a local system.
OPTIONS

View File

@@ -61,6 +61,9 @@ include::pretty-options.txt[]
the specified paths; this means that "<paths>..." limits only
commits, and doesn't limit diff for those commits.
--follow::
Continue listing the history of a file beyond renames.
<paths>...::
Show only commits that affect the specified paths.
@@ -91,6 +94,12 @@ git log -r --name-status release..test::
in the "release" branch, along with the list of paths
each commit modifies.
git log --follow builtin-rev-list.c::
Shows the commits that changed builtin-rev-list.c, including
those commits that occurred before the file was given its
present name.
Discussion
----------

View File

@@ -43,6 +43,21 @@ If you tried a merge which resulted in a complex conflicts and
would want to start over, you can recover with
gitlink:git-reset[1].
CONFIGURATION
-------------
merge.summary::
Whether to include summaries of merged commits in newly
created merge commit. False by default.
merge.verbosity::
Controls the amount of output shown by the recursive merge
strategy. Level 0 outputs nothing except a final error
message if conflicts were detected. Level 1 outputs only
conflicts, 2 outputs conflicts and file changes. Level 5 and
above outputs debugging information. The default is level 2.
Can be overriden by 'GIT_MERGE_VERBOSITY' environment variable.
HOW MERGE WORKS
---------------

View File

@@ -85,6 +85,17 @@ base-name::
times to get to the necessary object.
The default value for --window is 10 and --depth is 50.
--window-memory=[N]::
This option provides an additional limit on top of `--window`;
the window size will dynamically scale down so as to not take
up more than N bytes in memory. This is useful in
repositories with a mix of large and small objects to not run
out of memory with a large window, but still be able to take
advantage of the large window for the smaller objects. The
size can be suffixed with "k", "m", or "g".
`--window-memory=0` makes memory usage unlimited, which is the
default.
--max-pack-size=<n>::
Maximum size of each output packfile, expressed in MiB.
If specified, multiple packfiles may be created.

View File

@@ -29,7 +29,7 @@ include::fetch-options.txt[]
include::pull-fetch-param.txt[]
include::urls.txt[]
include::urls-remotes.txt[]
include::merge-strategies.txt[]

View File

@@ -95,7 +95,7 @@ the remote repository.
-v::
Run verbosely.
include::urls.txt[]
include::urls-remotes.txt[]
Examples

View File

@@ -8,7 +8,8 @@ git-rebase - Forward-port local commits to the updated upstream head
SYNOPSIS
--------
[verse]
'git-rebase' [-v] [--merge] [-C<n>] [--onto <newbase>] <upstream> [<branch>]
'git-rebase' [-i | --interactive] [-v | --verbose] [--merge] [-C<n>]
[-p | --preserve-merges] [--onto <newbase>] <upstream> [<branch>]
'git-rebase' --continue | --skip | --abort
DESCRIPTION
@@ -208,6 +209,14 @@ OPTIONS
context exist they all must match. By default no context is
ever ignored.
-i, \--interactive::
Make a list of the commits which are about to be rebased. Let the
user edit that list before rebasing.
-p, \--preserve-merges::
Instead of ignoring merges, try to recreate them. This option
only works in interactive mode.
include::merge-strategies.txt[]
NOTES
@@ -226,9 +235,100 @@ pre-rebase hook script for an example.
You must be in the top directory of your project to start (or continue)
a rebase. Upon completion, <branch> will be the current branch.
Author
INTERACTIVE MODE
----------------
Rebasing interactively means that you have a chance to edit the commits
which are rebased. You can reorder the commits, and you can
remove them (weeding out bad or otherwise unwanted patches).
The interactive mode is meant for this type of workflow:
1. have a wonderful idea
2. hack on the code
3. prepare a series for submission
4. submit
where point 2. consists of several instances of
a. regular use
1. finish something worthy of a commit
2. commit
b. independent fixup
1. realize that something does not work
2. fix that
3. commit it
Sometimes the thing fixed in b.2. cannot be amended to the not-quite
perfect commit it fixes, because that commit is buried deeply in a
patch series. That is exactly what interactive rebase is for: use it
after plenty of "a"s and "b"s, by rearranging and editing
commits, and squashing multiple commits into one.
Start it with the last commit you want to retain as-is:
git rebase -i <after-this-commit>
An editor will be fired up with all the commits in your current branch
(ignoring merge commits), which come after the given commit. You can
reorder the commits in this list to your heart's content, and you can
remove them. The list looks more or less like this:
-------------------------------------------
pick deadbee The oneline of this commit
pick fa1afe1 The oneline of the next commit
...
-------------------------------------------
The oneline descriptions are purely for your pleasure; `git-rebase` will
not look at them but at the commit names ("deadbee" and "fa1afe1" in this
example), so do not delete or edit the names.
By replacing the command "pick" with the command "edit", you can tell
`git-rebase` to stop after applying that commit, so that you can edit
the files and/or the commit message, amend the commit, and continue
rebasing.
If you want to fold two or more commits into one, replace the command
"pick" with "squash" for the second and subsequent commit. If the
commits had different authors, it will attribute the squashed commit to
the author of the last commit.
In both cases, or when a "pick" does not succeed (because of merge
errors), the loop will stop to let you fix things, and you can continue
the loop with `git rebase --continue`.
For example, if you want to reorder the last 5 commits, such that what
was HEAD~4 becomes the new HEAD. To achieve that, you would call
`git-rebase` like this:
----------------------
$ git rebase -i HEAD~5
----------------------
And move the first patch to the end of the list.
You might want to preserve merges, if you have a history like this:
------------------
X
\
A---M---B
/
---o---O---P---Q
------------------
Suppose you want to rebase the side branch starting at "A" to "Q". Make
sure that the current HEAD is "B", and call
-----------------------------
$ git rebase -i -p --onto Q O
-----------------------------
Authors
------
Written by Junio C Hamano <junkio@cox.net>
Written by Junio C Hamano <junkio@cox.net> and
Johannes E. Schindelin <johannes.schindelin@gmx.de>
Documentation
--------------

View File

@@ -48,8 +48,8 @@ standard input of the hook will be one line per ref to be updated:
The refname value is relative to $GIT_DIR; e.g. for the master
head this is "refs/heads/master". The two sha1 values before
each refname are the object names for the refname before and after
the update. Refs to be created will have sha1-old equal to 0{40},
while refs to be deleted will have sha1-new equal to 0{40}, otherwise
the update. Refs to be created will have sha1-old equal to 0\{40},
while refs to be deleted will have sha1-new equal to 0\{40}, otherwise
sha1-old and sha1-new should be valid objects in the repository.
This hook is called before any refname is updated and before any
@@ -71,7 +71,7 @@ The refname parameter is relative to $GIT_DIR; e.g. for the master
head this is "refs/heads/master". The two sha1 arguments are
the object names for the refname before and after the update.
Note that the hook is called before the refname is updated,
so either sha1-old is 0{40} (meaning there is no such ref yet),
so either sha1-old is 0\{40} (meaning there is no such ref yet),
or it should match what is recorded in refname.
The hook should exit with non-zero status if it wants to disallow
@@ -96,8 +96,8 @@ The refname value is relative to $GIT_DIR; e.g. for the master
head this is "refs/heads/master". The two sha1 values before
each refname are the object names for the refname before and after
the update. Refs that were created will have sha1-old equal to
0{40}, while refs that were deleted will have sha1-new equal to
0{40}, otherwise sha1-old and sha1-new should be valid objects in
0\{40}, while refs that were deleted will have sha1-new equal to
0\{40}, otherwise sha1-old and sha1-new should be valid objects in
the repository.
Using this hook, it is easy to generate mails describing the updates

View File

@@ -68,6 +68,17 @@ OPTIONS
to be applied that many times to get to the necessary object.
The default value for --window is 10 and --depth is 50.
--window-memory=[N]::
This option provides an additional limit on top of `--window`;
the window size will dynamically scale down so as to not take
up more than N bytes in memory. This is useful in
repositories with a mix of large and small objects to not run
out of memory with a large window, but still be able to take
advantage of the large window for the smaller objects. The
size can be suffixed with "k", "m", or "g".
`--window-memory=0` makes memory usage unlimited, which is the
default.
--max-pack-size=<n>::
Maximum size of each output packfile, expressed in MiB.
If specified, multiple packfiles may be created.

View File

@@ -23,7 +23,7 @@ initial manual merge, and later by noticing the same automerge
results and applying the previously recorded hand resolution.
[NOTE]
You need to create `$GIT_DIR/rr-cache` directory to enable this
You need to set the config variable rerere.enabled to enable this
command.
@@ -171,7 +171,7 @@ records it if it is a new conflict, or reuses the earlier hand
resolve when it is not. `git-commit` also invokes `git-rerere`
when recording a merge result. What this means is that you do
not have to do anything special yourself (Note: you still have
to create `$GIT_DIR/rr-cache` directory to enable this command).
to set the config variable rerere.enabled to enable this command).
In our example, when you did the test merge, the manual
resolution is recorded, and it will be reused when you do the

View File

@@ -28,7 +28,7 @@ SYNOPSIS
[ \--encoding[=<encoding>] ]
[ \--(author|committer|grep)=<pattern> ]
[ \--regexp-ignore-case ] [ \--extended-regexp ]
[ \--date={local|relative|default} ]
[ \--date={local|relative|default|iso|rfc|short} ]
[ [\--objects | \--objects-edge] [ \--unpacked ] ]
[ \--pretty | \--header ]
[ \--bisect ]
@@ -96,7 +96,7 @@ include::pretty-options.txt[]
Synonym for `--date=relative`.
--date={relative,local,default}::
--date={relative,local,default,iso,rfc}::
Only takes effect for dates shown in human-readable format, such
as when using "--pretty".
@@ -106,6 +106,13 @@ e.g. "2 hours ago".
+
`--date=local` shows timestamps in user's local timezone.
+
`--date=iso` (or `--date=iso8601`) shows timestamps in ISO 8601 format.
+
`--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822
format, often found in E-mail messages.
+
`--date=short` shows only date but not time, in `YYYY-MM-DD` fomat.
+
`--date=default` shows timestamps in the original timezone
(either committer's or author's).
@@ -284,9 +291,9 @@ excluded from the output.
+
With '\--pretty' format other than oneline (for obvious reasons),
this causes the output to have two extra lines of information
taken from the reflog. By default, 'commit@{Nth}' notation is
taken from the reflog. By default, 'commit@\{Nth}' notation is
used in the output. When the starting commit is specified as
'commit@{now}', output also uses 'commit@{timestamp}' notation
'commit@{now}', output also uses 'commit@\{timestamp}' notation
instead. Under '\--pretty=oneline', the commit message is
prefixed with this information on the same line.

View File

@@ -14,7 +14,8 @@ DESCRIPTION
Remove files from the working tree and from the index. The
files have to be identical to the tip of the branch, and no
updates to its contents must have been placed in the staging
area (aka index).
area (aka index). When --cached is given, the staged content has to
match either the tip of the branch *or* the file on disk.
OPTIONS

View File

@@ -13,6 +13,8 @@ SYNOPSIS
DESCRIPTION
-----------
THIS COMMAND IS DEPRECATED.
Pulls from a remote repository over ssh connection, invoking
git-ssh-upload on the other end. It functions identically to
git-ssh-upload, aside from which end you run it on.

View File

@@ -12,6 +12,8 @@ SYNOPSIS
DESCRIPTION
-----------
THIS COMMAND IS DEPRECATED.
Pushes from a remote repository over ssh connection, invoking
git-ssh-fetch on the other end. It functions identically to
git-ssh-fetch, aside from which end you run it on.

162
Documentation/git-stash.txt Normal file
View File

@@ -0,0 +1,162 @@
git-stash(1)
============
NAME
----
git-stash - Stash the changes in a dirty working directory away
SYNOPSIS
--------
[verse]
'git-stash' (save | list | show [<stash>] | apply [<stash>] | clear)
DESCRIPTION
-----------
Use 'git-stash' when you want to record the current state of the
working directory and the index, but want to go back to a clean
working directory. The command saves your local modifications away
and reverts the working directory to match the `HEAD` commit.
The modifications stashed away by this command can be listed with
`git-stash list`, inspected with `git-stash show`, and restored
(potentially on top of a different commit) with `git-stash apply`.
Calling git-stash without any arguments is equivalent to `git-stash
save`.
The latest stash you created is stored in `$GIT_DIR/refs/stash`; older
stashes are found in the reflog of this reference and can be named using
the usual reflog syntax (e.g. `stash@\{1}` is the most recently
created stash, `stash@\{2}` is the one before it, `stash@\{2.hours.ago}`
is also possible).
OPTIONS
-------
save::
Save your local modifications to a new 'stash', and run `git-reset
--hard` to revert them. This is the default action when no
subcommand is given.
list::
List the stashes that you currently have. Each 'stash' is listed
with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1} is
the one before, etc.), the name of the branch that was current when the
stash was made, and a short description of the commit the stash was
based on.
+
----------------------------------------------------------------
stash@{0}: submit: 6ebd0e2... Add git-stash
stash@{1}: master: 9cc0589... Merge branch 'master' of gfi
----------------------------------------------------------------
show [<stash>]::
Show the changes recorded in the stash as a diff between the the
stashed state and its original parent. When no `<stash>` is given,
shows the latest one. By default, the command shows the diffstat, but
it will accept any format known to `git-diff` (e.g., `git-stash show
-p stash@\{2}` to view the second most recent stash in patch form).
apply [<stash>]::
Restore the changes recorded in the stash on top of the current
working tree state. When no `<stash>` is given, applies the latest
one. The working directory must match the index.
+
This operation can fail with conflicts; you need to resolve them
by hand in the working tree.
clear::
Remove all the stashed states. Note that those states will then
be subject to pruning, and may be difficult or impossible to recover.
DISCUSSION
----------
A stash is represented as a commit whose tree records the state of the
working directory, and its first parent is the commit at `HEAD` when
the stash was created. The tree of the second parent records the
state of the index when the stash is made, and it is made a child of
the `HEAD` commit. The ancestry graph looks like this:
.----W
/ /
-----H----I
where `H` is the `HEAD` commit, `I` is a commit that records the state
of the index, and `W` is a commit that records the state of the working
tree.
EXAMPLES
--------
Pulling into a dirty tree::
When you are in the middle of something, you learn that there are
upstream changes that are possibly relevant to what you are
doing. When your local changes do not conflict with the changes in
the upstream, a simple `git pull` will let you move forward.
+
However, there are cases in which your local changes do conflict with
the upstream changes, and `git pull` refuses to overwrite your
changes. In such a case, you can stash your changes away,
perform a pull, and then unstash, like this:
+
----------------------------------------------------------------
$ git pull
...
file foobar not up to date, cannot merge.
$ git stash
$ git pull
$ git stash apply
----------------------------------------------------------------
Interrupted workflow::
When you are in the middle of something, your boss comes in and
demands that you fix something immediately. Traditionally, you would
make a commit to a temporary branch to store your changes away, and
return to your original branch to make the emergency fix, like this:
+
----------------------------------------------------------------
... hack hack hack ...
$ git checkout -b my_wip
$ git commit -a -m "WIP"
$ git checkout master
$ edit emergency fix
$ git commit -a -m "Fix in a hurry"
$ git checkout my_wip
$ git reset --soft HEAD^
... continue hacking ...
----------------------------------------------------------------
+
You can use `git-stash` to simplify the above, like this:
+
----------------------------------------------------------------
... hack hack hack ...
$ git stash
$ edit emergency fix
$ git commit -a -m "Fix in a hurry"
$ git stash apply
... continue hacking ...
----------------------------------------------------------------
SEE ALSO
--------
gitlink:git-checkout[1],
gitlink:git-commit[1],
gitlink:git-reflog[1],
gitlink:git-reset[1]
AUTHOR
------
Written by Nanako Shiraishi <nanako3@bluebottle.com>
GIT
---
Part of the gitlink:git[7] suite

View File

@@ -8,11 +8,20 @@ git-submodule - Initialize, update or inspect submodules
SYNOPSIS
--------
[verse]
'git-submodule' [--quiet] [-b branch] add <repository> [<path>]
'git-submodule' [--quiet] [--cached] [status|init|update] [--] [<path>...]
COMMANDS
--------
add::
Add the given repository as a submodule at the given path
to the changeset to be committed next. In particular, the
repository is cloned at the specified path, added to the
changeset and registered in .gitmodules. If no path is
specified, the path is deduced from the repository specification.
status::
Show the status of the submodules. This will print the SHA-1 of the
currently checked out commit for each submodule, along with the
@@ -24,8 +33,8 @@ status::
init::
Initialize the submodules, i.e. register in .git/config each submodule
path and url found in .gitmodules. The key used in git/config is
`submodule.$path.url`. This command does not alter existing information
name and url found in .gitmodules. The key used in .git/config is
`submodule.$name.url`. This command does not alter existing information
in .git/config.
update::
@@ -39,6 +48,9 @@ OPTIONS
-q, --quiet::
Only print error messages.
-b, --branch::
Branch of repository to add as submodule.
--cached::
Display the SHA-1 stored in the index, not the SHA-1 of the currently
checked out submodule commit. This option is only valid for the
@@ -53,7 +65,7 @@ FILES
When initializing submodules, a .gitmodules file in the top-level directory
of the containing repository is used to find the url of each submodule.
This file should be formatted in the same way as $GIR_DIR/config. The key
to each submodule url is "module.$path.url".
to each submodule url is "submodule.$name.url".
AUTHOR

View File

@@ -42,9 +42,11 @@ unreleased) version of git, that is available from 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
* link:v1.5.2.2/git.html[documentation for release 1.5.2.2]
* link:v1.5.2.4/git.html[documentation for release 1.5.2.4]
* release notes for
link:RelNotes-1.5.2.4.txt[1.5.2.4],
link:RelNotes-1.5.2.3.txt[1.5.2.3],
link:RelNotes-1.5.2.2.txt[1.5.2.2],
link:RelNotes-1.5.2.1.txt[1.5.2.1],
link:RelNotes-1.5.2.txt[1.5.2].
@@ -409,6 +411,11 @@ parameter, <path>.
other
~~~~~
'GIT_MERGE_VERBOSITY'::
A number controlling the amount of output shown by
the recursive merge strategy. Overrides merge.verbosity.
See gitlink:git-merge[1]
'GIT_PAGER'::
This environment variable overrides `$PAGER`.

View File

@@ -72,8 +72,8 @@ EFFECTS
-------
Certain operations by git can be influenced by assigning
particular attributes to a path. Currently, three operations
are attributes-aware.
particular attributes to a path. Currently, the following
operations are attributes-aware.
Checking-out and checking-in
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -199,7 +199,9 @@ Generating diff text
~~~~~~~~~~~~~~~~~~~~
The attribute `diff` affects if `git diff` generates textual
patch for the path or just says `Binary files differ`.
patch for the path or just says `Binary files differ`. It also
can affect what line is shown on the hunk header `@@ -k,l +n,m @@`
line.
Set::
@@ -224,7 +226,8 @@ String::
Diff is shown using the specified custom diff driver.
The driver program is given its input using the same
calling convention as used for GIT_EXTERNAL_DIFF
program.
program. This name is also used for custom hunk header
selection.
Defining a custom diff driver
@@ -249,6 +252,50 @@ parameters, just like `GIT_EXTERNAL_DIFF` program is called.
See gitlink:git[7] for details.
Defining a custom hunk-header
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Each group of changes (called "hunk") in the textual diff output
is prefixed with a line of the form:
@@ -k,l +n,m @@ TEXT
The text is called 'hunk header', and by default a line that
begins with an alphabet, an underscore or a dollar sign is used,
which matches what GNU `diff -p` output uses. This default
selection however is not suited for some contents, and you can
use customized pattern to make a selection.
First in .gitattributes, you would assign the `diff` attribute
for paths.
------------------------
*.tex diff=tex
------------------------
Then, you would define "diff.tex.funcname" configuration to
specify a regular expression that matches a line that you would
want to appear as the hunk header, like this:
------------------------
[diff "tex"]
funcname = "^\\(\\\\\\(sub\\)*section{.*\\)$"
------------------------
Note. A single level of backslashes are eaten by the
configuration file parser, so you would need to double the
backslashes; the pattern above picks a line that begins with a
backslash, and zero or more occurences of `sub` followed by
`section` followed by open brace, to the end of line.
There are a few built-in patterns to make this easier, and `tex`
is one of them, so you do not have to write the above in your
configuration file (you still need to enable this with the
attribute mechanism, via `.gitattributes`). Another built-in
pattern is defined for `java` that defines a pattern suitable
for program text in Java language.
Performing a three-way merge
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -10,7 +10,7 @@ USAGE='<refname> <target directory>'
export GIT_DIR
test -z "$mandir" && usage
if ! git-rev-parse --verify "$head^0" >/dev/null; then
if ! git rev-parse --verify "$head^0" >/dev/null; then
echo >&2 "head: $head does not exist in the current repository"
usage
fi
@@ -18,12 +18,12 @@ fi
GIT_INDEX_FILE=`pwd`/.quick-doc.index
export GIT_INDEX_FILE
rm -f "$GIT_INDEX_FILE"
git-read-tree $head
git-checkout-index -a -f --prefix="$mandir"/
git read-tree $head
git checkout-index -a -f --prefix="$mandir"/
if test -n "$GZ"; then
cd "$mandir"
for i in `git-ls-tree -r --name-only $head`
for i in `git ls-tree -r --name-only $head`
do
gzip < $i > $i.gz && rm $i
done

View File

@@ -106,12 +106,14 @@ The placeholders are:
- '%aD': author date, RFC2822 style
- '%ar': author date, relative
- '%at': author date, UNIX timestamp
- '%ai': author date, ISO 8601 format
- '%cn': committer name
- '%ce': committer email
- '%cd': committer date
- '%cD': committer date, RFC2822 style
- '%cr': committer date, relative
- '%ct': committer date, UNIX timestamp
- '%ci': committer date, ISO 8601 format
- '%e': encoding
- '%s': subject
- '%b': body

View File

@@ -354,7 +354,7 @@ used for pulls:
-------------------------------------
$ git config --get remote.origin.url
/home/bob/myrepo
/home/alice/project
-------------------------------------
(The complete configuration created by git-clone is visible using

View File

@@ -0,0 +1,55 @@
include::urls.txt[]
REMOTES
-------
In addition to the above, as a short-hand, the name of a
file in `$GIT_DIR/remotes` directory can be given; the
named file should be in the following format:
------------
URL: one of the above URL format
Push: <refspec>
Pull: <refspec>
------------
Then such a short-hand is specified in place of
<repository> without <refspec> parameters on the command
line, <refspec> specified on `Push:` lines or `Pull:`
lines are used for `git-push` and `git-fetch`/`git-pull`,
respectively. Multiple `Push:` and `Pull:` lines may
be specified for additional branch mappings.
Or, equivalently, in the `$GIT_DIR/config` (note the use
of `fetch` instead of `Pull:`):
------------
[remote "<remote>"]
url = <url>
push = <refspec>
fetch = <refspec>
------------
The name of a file in `$GIT_DIR/branches` directory can be
specified as an older notation short-hand; the named
file should contain a single line, a URL in one of the
above formats, optionally followed by a hash `#` and the
name of remote head (URL fragment notation).
`$GIT_DIR/branches/<remote>` file that stores a <url>
without the fragment is equivalent to have this in the
corresponding file in the `$GIT_DIR/remotes/` directory.
------------
URL: <url>
Pull: refs/heads/master:<remote>
------------
while having `<url>#<head>` is equivalent to
------------
URL: <url>
Pull: refs/heads/<head>:<remote>
------------

View File

@@ -32,57 +32,3 @@ To sync with a local directory, use:
===============================================================
- /path/to/repo.git/
===============================================================
REMOTES
-------
In addition to the above, as a short-hand, the name of a
file in `$GIT_DIR/remotes` directory can be given; the
named file should be in the following format:
------------
URL: one of the above URL format
Push: <refspec>
Pull: <refspec>
------------
Then such a short-hand is specified in place of
<repository> without <refspec> parameters on the command
line, <refspec> specified on `Push:` lines or `Pull:`
lines are used for `git-push` and `git-fetch`/`git-pull`,
respectively. Multiple `Push:` and `Pull:` lines may
be specified for additional branch mappings.
Or, equivalently, in the `$GIT_DIR/config` (note the use
of `fetch` instead of `Pull:`):
------------
[remote "<remote>"]
url = <url>
push = <refspec>
fetch = <refspec>
------------
The name of a file in `$GIT_DIR/branches` directory can be
specified as an older notation short-hand; the named
file should contain a single line, a URL in one of the
above formats, optionally followed by a hash `#` and the
name of remote head (URL fragment notation).
`$GIT_DIR/branches/<remote>` file that stores a <url>
without the fragment is equivalent to have this in the
corresponding file in the `$GIT_DIR/remotes/` directory.
------------
URL: <url>
Pull: refs/heads/master:<remote>
------------
while having `<url>#<head>` is equivalent to
------------
URL: <url>
Pull: refs/heads/<head>:<remote>
------------

View File

@@ -2974,13 +2974,13 @@ developed. If you blow the directory cache away entirely, you generally
haven't lost any information as long as you have the name of the tree
that it described.
At the same time, the index is at the same time also the
staging area for creating new trees, and creating a new tree always
involves a controlled modification of the index file. In particular,
the index file can have the representation of an intermediate tree that
has not yet been instantiated. So the index can be thought of as a
write-back cache, which can contain dirty information that has not yet
been written back to the backing store.
At the same time, the index is also the staging area for creating
new trees, and creating a new tree always involves a controlled
modification of the index file. In particular, the index file can
have the representation of an intermediate tree that has not yet been
instantiated. So the index can be thought of as a write-back cache,
which can contain dirty information that has not yet been written back
to the backing store.

View File

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

100
Makefile
View File

@@ -112,8 +112,6 @@ all:
# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
# MakeMaker (e.g. using ActiveState under Cygwin).
#
# Define WITH_P4IMPORT to build and install Python git-p4import script.
#
# Define NO_TCLTK if you do not want Tcl/Tk GUI.
#
# The TCL_PATH variable governs the location of the Tcl interpreter
@@ -176,6 +174,7 @@ export prefix bindir gitexecdir sharedir template_dir sysconfdir
CC = gcc
AR = ar
RM = rm -f
TAR = tar
INSTALL = install
RPMBUILD = rpmbuild
@@ -204,7 +203,7 @@ SCRIPT_SH = \
git-fetch.sh \
git-ls-remote.sh \
git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
git-pull.sh git-rebase.sh \
git-pull.sh git-rebase.sh git-rebase--interactive.sh \
git-repack.sh git-request-pull.sh git-reset.sh \
git-sh-setup.sh \
git-tag.sh git-verify-tag.sh \
@@ -212,7 +211,8 @@ SCRIPT_SH = \
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
git-merge-resolve.sh git-merge-ours.sh \
git-lost-found.sh git-quiltimport.sh git-submodule.sh \
git-filter-branch.sh
git-filter-branch.sh \
git-stash.sh
SCRIPT_PERL = \
git-add--interactive.perl \
@@ -221,20 +221,9 @@ SCRIPT_PERL = \
git-svnimport.perl git-cvsexportcommit.perl \
git-send-email.perl git-svn.perl
SCRIPT_PYTHON = \
git-p4import.py
ifdef WITH_P4IMPORT
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
$(patsubst %.py,%,$(SCRIPT_PYTHON)) \
git-status git-instaweb
else
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
git-status git-instaweb
endif
# ... and all the rest that could be moved out of bindir to gitexecdir
PROGRAMS = \
@@ -283,9 +272,6 @@ endif
ifndef PERL_PATH
PERL_PATH = /usr/bin/perl
endif
ifndef PYTHON_PATH
PYTHON_PATH = /usr/local/bin/python
endif
export PERL_PATH
@@ -739,7 +725,6 @@ prefix_SQ = $(subst ','\'',$(prefix))
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
LIBS = $(GITLIBS) $(EXTLIBS)
@@ -769,7 +754,7 @@ strip: $(PROGRAMS) git$X
$(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
gitk-wish: gitk GIT-GUI-VARS
$(QUIET_GEN)rm -f $@ $@+ && \
$(QUIET_GEN)$(RM) $@ $@+ && \
sed -e '1,3s|^exec .* "$$0"|exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' <gitk >$@+ && \
chmod +x $@+ && \
mv -f $@+ $@
@@ -779,17 +764,16 @@ git.o: git.c common-cmds.h GIT-CFLAGS
$(ALL_CFLAGS) -c $(filter %.c,$^)
git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
$(QUIET_LINK)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
$(ALL_CFLAGS) -o $@ $(filter %.c,$^) git.o \
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
$(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
help.o: common-cmds.h
git-merge-subtree$X: git-merge-recursive$X
$(QUIET_BUILT_IN)rm -f $@ && ln git-merge-recursive$X $@
$(QUIET_BUILT_IN)$(RM) $@ && ln git-merge-recursive$X $@
$(BUILT_INS): git$X
$(QUIET_BUILT_IN)rm -f $@ && ln git$X $@
$(QUIET_BUILT_IN)$(RM) $@ && ln git$X $@
common-cmds.h: ./generate-cmdlist.sh
@@ -797,7 +781,7 @@ common-cmds.h: $(wildcard Documentation/git-*.txt)
$(QUIET_GEN)./generate-cmdlist.sh > $@+ && mv $@+ $@
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
$(QUIET_GEN)rm -f $@ $@+ && \
$(QUIET_GEN)$(RM) $@ $@+ && \
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
@@ -808,20 +792,11 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
$(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
rm -f $@ $@+
sed -e '1s|#!.*/python|#!$(PYTHON_PATH_SQ)|' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
$@.py >$@+
chmod +x $@+
mv $@+ $@
perl/perl.mak: GIT-CFLAGS
$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
$(QUIET_GEN)rm -f $@ $@+ && \
$(QUIET_GEN)$(RM) $@ $@+ && \
INSTLIBDIR=`$(MAKE) -C perl -s --no-print-directory instlibdir` && \
sed -e '1{' \
-e ' s|#!.*perl|#!$(PERL_PATH_SQ)|' \
@@ -840,7 +815,7 @@ git-status: git-commit
$(QUIET_GEN)cp $< $@+ && mv $@+ $@
gitweb/gitweb.cgi: gitweb/gitweb.perl
$(QUIET_GEN)rm -f $@ $@+ && \
$(QUIET_GEN)$(RM) $@ $@+ && \
sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
-e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
-e 's|++GIT_BINDIR++|$(bindir)|g' \
@@ -863,7 +838,7 @@ gitweb/gitweb.cgi: gitweb/gitweb.perl
mv $@+ $@
git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
$(QUIET_GEN)rm -f $@ $@+ && \
$(QUIET_GEN)$(RM) $@ $@+ && \
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
@@ -876,14 +851,14 @@ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
mv $@+ $@
configure: configure.ac
$(QUIET_GEN)rm -f $@ $<+ && \
$(QUIET_GEN)$(RM) $@ $<+ && \
sed -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
$< > $<+ && \
autoconf -o $@ $<+ && \
rm -f $<+
$(RM) $<+
# These can record GIT_VERSION
git$X git.spec \
git.o git.spec \
$(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
: GIT-VERSION-FILE
@@ -938,7 +913,7 @@ $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
$(DIFF_OBJS): diffcore.h
$(LIB_FILE): $(LIB_OBJS)
$(QUIET_AR)rm -f $@ && $(AR) rcs $@ $(LIB_OBJS)
$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
xdiff/xmerge.o
@@ -946,7 +921,7 @@ $(XDIFF_OBJS): xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \
xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h
$(XDIFF_LIB): $(XDIFF_OBJS)
$(QUIET_AR)rm -f $@ && $(AR) rcs $@ $(XDIFF_OBJS)
$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(XDIFF_OBJS)
perl/Makefile: perl/Git.pm perl/Makefile.PL GIT-CFLAGS
@@ -957,11 +932,11 @@ doc:
$(MAKE) -C Documentation all
TAGS:
rm -f TAGS
$(RM) TAGS
find . -name '*.[hcS]' -print | xargs etags -a
tags:
rm -f tags
$(RM) tags
find . -name '*.[hcS]' -print | xargs ctags -a
### Detect prefix changes
@@ -1018,7 +993,8 @@ check-sha1:: test-sha1$X
check: common-cmds.h
for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done
remove-dashes:
./fixup-builtins $(BUILT_INS)
### Installation rules
@@ -1040,7 +1016,7 @@ endif
cp '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \
'$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X'; \
fi
$(foreach p,$(BUILT_INS), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;)
$(foreach p,$(BUILT_INS), $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;)
install-doc:
$(MAKE) -C Documentation install
@@ -1069,7 +1045,7 @@ dist: git.spec git-archive configure
$(GIT_TARNAME)/configure \
$(GIT_TARNAME)/version \
$(GIT_TARNAME)/git-gui/version
@rm -rf $(GIT_TARNAME)
@$(RM) -r $(GIT_TARNAME)
gzip -f -9 $(GIT_TARNAME).tar
rpm: dist
@@ -1078,13 +1054,13 @@ rpm: dist
htmldocs = git-htmldocs-$(GIT_VERSION)
manpages = git-manpages-$(GIT_VERSION)
dist-doc:
rm -fr .doc-tmp-dir
$(RM) -r .doc-tmp-dir
mkdir .doc-tmp-dir
$(MAKE) -C Documentation WEBDOC_DEST=../.doc-tmp-dir install-webdoc
cd .doc-tmp-dir && $(TAR) cf ../$(htmldocs).tar .
gzip -n -9 -f $(htmldocs).tar
:
rm -fr .doc-tmp-dir
$(RM) -r .doc-tmp-dir
mkdir -p .doc-tmp-dir/man1 .doc-tmp-dir/man5 .doc-tmp-dir/man7
$(MAKE) -C Documentation DESTDIR=./ \
man1dir=../.doc-tmp-dir/man1 \
@@ -1093,31 +1069,31 @@ dist-doc:
install
cd .doc-tmp-dir && $(TAR) cf ../$(manpages).tar .
gzip -n -9 -f $(manpages).tar
rm -fr .doc-tmp-dir
$(RM) -r .doc-tmp-dir
### Cleaning rules
clean:
rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
$(RM) *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
$(LIB_FILE) $(XDIFF_LIB)
rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X
rm -f $(TEST_PROGRAMS)
rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
rm -rf autom4te.cache
rm -f configure config.log config.mak.autogen config.mak.append config.status config.cache
rm -rf $(GIT_TARNAME) .doc-tmp-dir
rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
rm -f $(htmldocs).tar.gz $(manpages).tar.gz
rm -f gitweb/gitweb.cgi
$(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X
$(RM) $(TEST_PROGRAMS)
$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
$(RM) -r autom4te.cache
$(RM) configure config.log config.mak.autogen config.mak.append config.status config.cache
$(RM) -r $(GIT_TARNAME) .doc-tmp-dir
$(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
$(RM) $(htmldocs).tar.gz $(manpages).tar.gz
$(RM) gitweb/gitweb.cgi
$(MAKE) -C Documentation/ clean
$(MAKE) -C perl clean
$(MAKE) -C templates/ clean
$(MAKE) -C t/ clean
ifndef NO_TCLTK
rm -f gitk-wish
$(RM) gitk-wish
$(MAKE) -C git-gui clean
endif
rm -f GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS
$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS
.PHONY: all install clean strip
.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags .FORCE-GIT-CFLAGS

View File

@@ -518,8 +518,8 @@ static struct patch *compare_buffer(mmfile_t *file_p, mmfile_t *file_o,
xdemitcb_t ecb;
xpp.flags = xdl_opts;
memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = context;
xecfg.flags = 0;
ecb.outf = xdiff_outf;
ecb.priv = &state;
memset(&state, 0, sizeof(state));

View File

@@ -1,7 +1,7 @@
/*
* Builtin "git branch"
*
* Copyright (c) 2006 Kristian H<EFBFBD>gsberg <krh@redhat.com>
* Copyright (c) 2006 Kristian Høgsberg <krh@redhat.com>
* Based on git-branch.sh by Junio C Hamano.
*/
@@ -10,6 +10,7 @@
#include "refs.h"
#include "commit.h"
#include "builtin.h"
#include "remote.h"
static const char builtin_branch_usage[] =
"git-branch [-r] (-d | -D) <branchname> | [--track | --no-track] [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]]";
@@ -22,7 +23,7 @@ static const char builtin_branch_usage[] =
static const char *head;
static unsigned char head_sha1[20];
static int branch_track_remotes;
static int branch_track = 1;
static int branch_use_color;
static char branch_colors[][COLOR_MAXLEN] = {
@@ -67,7 +68,7 @@ static int git_branch_config(const char *var, const char *value)
return 0;
}
if (!strcmp(var, "branch.autosetupmerge"))
branch_track_remotes = git_config_bool(var, value);
branch_track = git_config_bool(var, value);
return git_default_config(var, value);
}
@@ -324,125 +325,70 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev)
free_ref_list(&ref_list);
}
static char *config_repo;
static char *config_remote;
static const char *start_ref;
struct tracking {
struct refspec spec;
char *src;
const char *remote;
int matches;
};
static int get_remote_branch_name(const char *value)
static int find_tracked_branch(struct remote *remote, void *priv)
{
const char *colon;
const char *end;
struct tracking *tracking = priv;
if (*value == '+')
value++;
colon = strchr(value, ':');
if (!colon)
return 0;
end = value + strlen(value);
/*
* Try an exact match first. I.e. handle the case where the
* value is "$anything:refs/foo/bar/baz" and start_ref is exactly
* "refs/foo/bar/baz". Then the name at the remote is $anything.
*/
if (!strcmp(colon + 1, start_ref)) {
/* Truncate the value before the colon. */
nfasprintf(&config_repo, "%.*s", colon - value, value);
return 1;
if (!remote_find_tracking(remote, &tracking->spec)) {
if (++tracking->matches == 1) {
tracking->src = tracking->spec.src;
tracking->remote = remote->name;
} else {
free(tracking->spec.src);
if (tracking->src) {
free(tracking->src);
tracking->src = NULL;
}
}
tracking->spec.src = NULL;
}
/*
* Is this a wildcard match?
*/
if ((end - 2 <= value) || end[-2] != '/' || end[-1] != '*' ||
(colon - 2 <= value) || colon[-2] != '/' || colon[-1] != '*')
return 0;
/*
* Value is "refs/foo/bar/<asterisk>:refs/baz/boa/<asterisk>"
* and start_ref begins with "refs/baz/boa/"; the name at the
* remote is refs/foo/bar/ with the remaining part of the
* start_ref. The length of the prefix on the RHS is (end -
* colon - 2), including the slash immediately before the
* asterisk.
*/
if ((strlen(start_ref) < end - colon - 2) ||
memcmp(start_ref, colon + 1, end - colon - 2))
return 0; /* does not match prefix */
/* Replace the asterisk with the remote branch name. */
nfasprintf(&config_repo, "%.*s%s",
(colon - 1) - value, value,
start_ref + (end - colon - 2));
return 1;
}
static int get_remote_config(const char *key, const char *value)
{
const char *var;
if (prefixcmp(key, "remote."))
return 0;
var = strrchr(key, '.');
if (var == key + 6 || strcmp(var, ".fetch"))
return 0;
/*
* Ok, we are looking at key == "remote.$foo.fetch";
*/
if (get_remote_branch_name(value))
nfasprintf(&config_remote, "%.*s", var - (key + 7), key + 7);
return 0;
}
static void set_branch_merge(const char *name, const char *config_remote,
const char *config_repo)
/*
* 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
* config.
*/
static int setup_tracking(const char *new_ref, const char *orig_ref)
{
char key[1024];
if (sizeof(key) <=
snprintf(key, sizeof(key), "branch.%s.remote", name))
die("what a long branch name you have!");
git_config_set(key, config_remote);
struct tracking tracking;
/*
* We do not have to check if we have enough space for
* the 'merge' key, since it's shorter than the
* previous 'remote' key, which we already checked.
*/
snprintf(key, sizeof(key), "branch.%s.merge", name);
git_config_set(key, config_repo);
}
if (strlen(new_ref) > 1024 - 7 - 7 - 1)
return error("Tracking not set up: name too long: %s",
new_ref);
static void set_branch_defaults(const char *name, const char *real_ref)
{
/*
* name is the name of new branch under refs/heads;
* real_ref is typically refs/remotes/$foo/$bar, where
* $foo is the remote name (there typically are no slashes)
* and $bar is the branch name we map from the remote
* (it could have slashes).
*/
start_ref = real_ref;
git_config(get_remote_config);
if (!config_repo && !config_remote &&
!prefixcmp(real_ref, "refs/heads/")) {
set_branch_merge(name, ".", real_ref);
printf("Branch %s set up to track local branch %s.\n",
name, real_ref);
}
memset(&tracking, 0, sizeof(tracking));
tracking.spec.dst = (char *)orig_ref;
if (for_each_remote(find_tracked_branch, &tracking) ||
!tracking.matches)
return 1;
if (config_repo && config_remote) {
set_branch_merge(name, config_remote, config_repo);
if (tracking.matches > 1)
return error("Not tracking: ambiguous information for ref %s",
orig_ref);
if (tracking.matches == 1) {
sprintf(key, "branch.%s.remote", new_ref);
git_config_set(key, tracking.remote ? tracking.remote : ".");
sprintf(key, "branch.%s.merge", new_ref);
git_config_set(key, tracking.src);
free(tracking.src);
printf("Branch %s set up to track remote branch %s.\n",
name, real_ref);
new_ref, orig_ref);
}
if (config_repo)
free(config_repo);
if (config_remote)
free(config_remote);
return 0;
}
static void create_branch(const char *name, const char *start_name,
@@ -505,7 +451,7 @@ static void create_branch(const char *name, const char *start_name,
automatically merges from there. So far, this is only done for
remotes registered via .git/config. */
if (real_ref && track)
set_branch_defaults(name, real_ref);
setup_tracking(name, real_ref);
if (write_ref_sha1(lock, sha1, msg) < 0)
die("Failed to write ref: %s.", strerror(errno));
@@ -564,7 +510,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
int i;
git_config(git_branch_config);
track = branch_track_remotes;
track = branch_track;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];

View File

@@ -1,3 +1,4 @@
#include "builtin.h"
#include "cache.h"
#include "object.h"
#include "commit.h"

View File

@@ -36,6 +36,7 @@
* of "-a" causing problems (not possible in the above example,
* but get used to it in scripting!).
*/
#include "builtin.h"
#include "cache.h"
#include "strbuf.h"
#include "quote.h"

View File

@@ -178,14 +178,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)
char *home = getenv("HOME");
if (home) {
char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
setenv("GIT_CONFIG", user_config, 1);
setenv(CONFIG_ENVIRONMENT, user_config, 1);
free(user_config);
} else {
die("$HOME not set");
}
}
else if (!strcmp(argv[1], "--system"))
setenv("GIT_CONFIG", git_etc_gitconfig(), 1);
setenv(CONFIG_ENVIRONMENT, git_etc_gitconfig(), 1);
else if (!strcmp(argv[1], "--null") || !strcmp(argv[1], "-z")) {
term = '\0';
delim = '\n';

View File

@@ -1,3 +1,4 @@
#include "builtin.h"
#include "cache.h"
#include "refs.h"
#include "commit.h"

View File

@@ -1,3 +1,4 @@
#include "builtin.h"
#include "cache.h"
#include "refs.h"
#include "object.h"
@@ -796,7 +797,7 @@ static struct ref_sort *default_sort(void)
return sort;
}
int cmd_for_each_ref(int ac, const char **av, char *prefix)
int cmd_for_each_ref(int ac, const char **av, const char *prefix)
{
int i, num_refs;
const char *format = NULL;

View File

@@ -1,3 +1,4 @@
#include "builtin.h"
#include "cache.h"
#include "commit.h"
#include "tree.h"
@@ -20,6 +21,7 @@ static int check_strict;
static int keep_cache_objects;
static unsigned char head_sha1[20];
static int errors_found;
static int write_lost_and_found;
static int verbose;
#define ERROR_OBJECT 01
#define ERROR_REACHABLE 02
@@ -138,6 +140,21 @@ static void check_unreachable_object(struct object *obj)
if (!obj->used) {
printf("dangling %s %s\n", typename(obj->type),
sha1_to_hex(obj->sha1));
if (write_lost_and_found) {
char *filename = git_path("lost-found/%s/%s",
obj->type == OBJ_COMMIT ? "commit" : "other",
sha1_to_hex(obj->sha1));
FILE *f;
if (safe_create_leading_directories(filename)) {
error("Could not create lost-found");
return;
}
if (!(f = fopen(filename, "w")))
die("Could not open %s", filename);
fprintf(f, "%s\n", sha1_to_hex(obj->sha1));
fclose(f);
}
return;
}
@@ -643,7 +660,7 @@ static const char fsck_usage[] =
"git-fsck [--tags] [--root] [[--unreachable] [--cache] [--full] "
"[--strict] [--verbose] <head-sha1>*]";
int cmd_fsck(int argc, char **argv, const char *prefix)
int cmd_fsck(int argc, const char **argv, const char *prefix)
{
int i, heads;
@@ -685,6 +702,12 @@ int cmd_fsck(int argc, char **argv, const char *prefix)
verbose = 1;
continue;
}
if (!strcmp(arg, "--lost-found")) {
check_full = 1;
include_reflogs = 0;
write_lost_and_found = 1;
continue;
}
if (*arg == '-')
usage(fsck_usage);
}

View File

@@ -10,6 +10,7 @@
* Copyright (c) 2006 Shawn O. Pearce
*/
#include "builtin.h"
#include "cache.h"
#include "run-command.h"

View File

@@ -184,7 +184,36 @@ static void copy_templates(const char *git_dir, int len, const char *template_di
closedir(dir);
}
static int create_default_files(const char *git_dir, const char *template_path)
/*
* Get the full path to the working tree specified in $GIT_WORK_TREE
* or NULL if no working tree is specified.
*/
static const char *get_work_tree(void)
{
const char *git_work_tree;
char cwd[PATH_MAX];
static char worktree[PATH_MAX];
git_work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
if (!git_work_tree)
return NULL;
if (!getcwd(cwd, sizeof(cwd)))
die("Unable to read current working directory");
if (chdir(git_work_tree))
die("Cannot change directory to specified working tree '%s'",
git_work_tree);
if (git_work_tree[0] != '/') {
if (!getcwd(worktree, sizeof(worktree)))
die("Unable to read current working directory");
git_work_tree = worktree;
}
if (chdir(cwd))
die("Cannot come back to cwd");
return git_work_tree;
}
static int create_default_files(const char *git_dir, const char *git_work_tree,
const char *template_path)
{
unsigned len = strlen(git_dir);
static char path[PATH_MAX];
@@ -263,7 +292,7 @@ static int create_default_files(const char *git_dir, const char *template_path)
}
git_config_set("core.filemode", filemode ? "true" : "false");
if (is_bare_repository()) {
if (is_bare_repository() && !git_work_tree) {
git_config_set("core.bare", "true");
}
else {
@@ -271,12 +300,14 @@ static int create_default_files(const char *git_dir, const char *template_path)
/* allow template config file to override the default */
if (log_all_ref_updates == -1)
git_config_set("core.logallrefupdates", "true");
if (git_work_tree)
git_config_set("core.worktree", git_work_tree);
}
return reinit;
}
static const char init_db_usage[] =
"git-init [--template=<template-directory>] [--shared]";
"git-init [-q | --quiet] [--template=<template-directory>] [--shared]";
/*
* If you want to, you can share the DB area with any number of branches.
@@ -287,10 +318,12 @@ static const char init_db_usage[] =
int cmd_init_db(int argc, const char **argv, const char *prefix)
{
const char *git_dir;
const char *git_work_tree;
const char *sha1_dir;
const char *template_dir = NULL;
char *path;
int len, i, reinit;
int quiet = 0;
for (i = 1; i < argc; i++, argv++) {
const char *arg = argv[1];
@@ -300,10 +333,14 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
shared_repository = PERM_GROUP;
else if (!prefixcmp(arg, "--shared="))
shared_repository = git_config_perm("arg", arg+9);
else if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet"))
quiet = 1;
else
usage(init_db_usage);
}
git_work_tree = get_work_tree();
/*
* Set up the default .git directory contents
*/
@@ -319,7 +356,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
*/
check_repository_format();
reinit = create_default_files(git_dir, template_dir);
reinit = create_default_files(git_dir, git_work_tree, template_dir);
/*
* And set up the object store.
@@ -346,10 +383,11 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
git_config_set("receive.denyNonFastforwards", "true");
}
printf("%s%s Git repository in %s/\n",
reinit ? "Reinitialized existing" : "Initialized empty",
shared_repository ? " shared" : "",
git_dir);
if (!quiet)
printf("%s%s Git repository in %s/\n",
reinit ? "Reinitialized existing" : "Initialized empty",
shared_repository ? " shared" : "",
git_dir);
return 0;
}

View File

@@ -16,6 +16,7 @@
#include "refs.h"
static int default_show_root = 1;
static const char *fmt_patch_subject_prefix = "PATCH";
/* this is in builtin-diff.c */
void add_head(struct rev_info *revs);
@@ -55,6 +56,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
rev->commit_format = CMIT_FMT_DEFAULT;
rev->verbose_header = 1;
rev->show_root_diff = default_show_root;
rev->subject_prefix = fmt_patch_subject_prefix;
argc = setup_revisions(argc, argv, rev, "HEAD");
if (rev->diffopt.pickaxe || rev->diffopt.filter)
rev->always_show_header = 0;
@@ -94,6 +96,12 @@ static int cmd_log_walk(struct rev_info *rev)
static int git_log_config(const char *var, const char *value)
{
if (!strcmp(var, "format.subjectprefix")) {
if (!value)
die("format.subjectprefix without value");
fmt_patch_subject_prefix = xstrdup(value);
return 0;
}
if (!strcmp(var, "log.showroot")) {
default_show_root = git_config_bool(var, value);
return 0;
@@ -290,6 +298,7 @@ static int git_format_config(const char *var, const char *value)
if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
return 0;
}
return git_log_config(var, value);
}
@@ -459,6 +468,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.diffopt.msg_sep = "";
rev.diffopt.recursive = 1;
rev.subject_prefix = fmt_patch_subject_prefix;
rev.extra_headers = extra_headers;
/*

View File

@@ -1,3 +1,4 @@
#include "builtin.h"
#include "cache.h"
#include "commit.h"

View File

@@ -1,3 +1,4 @@
#include "builtin.h"
#include "cache.h"
#include "xdiff/xdiff.h"
#include "xdiff-interface.h"
@@ -5,9 +6,9 @@
static const char merge_file_usage[] =
"git merge-file [-p | --stdout] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
int cmd_merge_file(int argc, char **argv, char **envp)
int cmd_merge_file(int argc, const char **argv, const char *prefix)
{
char *names[3];
const char *names[3];
mmfile_t mmfs[3];
mmbuffer_t result = {NULL, 0};
xpparam_t xpp = {XDF_NEED_MINIMAL};
@@ -51,7 +52,7 @@ int cmd_merge_file(int argc, char **argv, char **envp)
free(mmfs[i].ptr);
if (ret >= 0) {
char *filename = argv[1];
const char *filename = argv[1];
FILE *f = to_stdout ? stdout : fopen(filename, "wb");
if (!f)

View File

@@ -16,8 +16,9 @@
#include "progress.h"
static const char pack_usage[] = "\
git-pack-objects [{ -q | --progress | --all-progress }] [--max-pack-size=N] \n\
[--local] [--incremental] [--window=N] [--depth=N] \n\
git-pack-objects [{ -q | --progress | --all-progress }] \n\
[--max-pack-size=N] [--local] [--incremental] \n\
[--window=N] [--window-memory=N] [--depth=N] \n\
[--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
[--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
[--stdout | base-name] [<ref-list | <object-list]";
@@ -25,9 +26,6 @@ git-pack-objects [{ -q | --progress | --all-progress }] [--max-pack-size=N] \n\
struct object_entry {
struct pack_idx_entry idx;
unsigned long size; /* uncompressed size */
unsigned int hash; /* name hint hash */
unsigned int depth; /* delta depth */
struct packed_git *in_pack; /* already in pack */
off_t in_pack_offset;
struct object_entry *delta; /* delta base object */
@@ -37,6 +35,7 @@ struct object_entry {
*/
void *delta_data; /* cached delta (uncompressed) */
unsigned long delta_size; /* delta data size (uncompressed) */
unsigned int hash; /* name hint hash */
enum object_type type;
enum object_type in_pack_type; /* could be delta */
unsigned char in_pack_header_size;
@@ -79,6 +78,9 @@ static unsigned long delta_cache_size = 0;
static unsigned long max_delta_cache_size = 0;
static unsigned long cache_max_small_delta_size = 1000;
static unsigned long window_memory_usage = 0;
static unsigned long window_memory_limit = 0;
/*
* The object names in objects array are hashed with this hashtable,
* to help looking up the entry by object name.
@@ -1270,6 +1272,7 @@ struct unpacked {
struct object_entry *entry;
void *data;
struct delta_index *index;
unsigned depth;
};
static int delta_cacheable(struct unpacked *trg, struct unpacked *src,
@@ -1303,6 +1306,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
struct object_entry *trg_entry = trg->entry;
struct object_entry *src_entry = src->entry;
unsigned long trg_size, src_size, delta_size, sizediff, max_size, sz;
unsigned ref_depth;
enum object_type type;
void *delta_buf;
@@ -1327,21 +1331,28 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
return 0;
/* Let's not bust the allowed depth. */
if (src_entry->depth >= max_depth)
if (src->depth >= max_depth)
return 0;
/* Now some size filtering heuristics. */
trg_size = trg_entry->size;
max_size = trg_size/2 - 20;
max_size = max_size * (max_depth - src_entry->depth) / max_depth;
if (!trg_entry->delta) {
max_size = trg_size/2 - 20;
ref_depth = 1;
} else {
max_size = trg_entry->delta_size;
ref_depth = trg->depth;
}
max_size = max_size * (max_depth - src->depth) /
(max_depth - ref_depth + 1);
if (max_size == 0)
return 0;
if (trg_entry->delta && trg_entry->delta_size <= max_size)
max_size = trg_entry->delta_size-1;
src_size = src_entry->size;
sizediff = src_size < trg_size ? trg_size - src_size : 0;
if (sizediff >= max_size)
return 0;
if (trg_size < src_size / 32)
return 0;
/* Load data if not already done */
if (!trg->data) {
@@ -1349,12 +1360,14 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
if (sz != trg_size)
die("object %s inconsistent object length (%lu vs %lu)",
sha1_to_hex(trg_entry->idx.sha1), sz, trg_size);
window_memory_usage += sz;
}
if (!src->data) {
src->data = read_sha1_file(src_entry->idx.sha1, &type, &sz);
if (sz != src_size)
die("object %s inconsistent object length (%lu vs %lu)",
sha1_to_hex(src_entry->idx.sha1), sz, src_size);
window_memory_usage += sz;
}
if (!src->index) {
src->index = create_delta_index(src->data, src_size);
@@ -1364,6 +1377,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
warning("suboptimal pack - out of memory");
return 0;
}
window_memory_usage += sizeof_delta_index(src->index);
}
delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size);
@@ -1371,13 +1385,19 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
return 0;
if (trg_entry->delta_data) {
/* Prefer only shallower same-sized deltas. */
if (delta_size == trg_entry->delta_size &&
src->depth + 1 >= trg->depth) {
free(delta_buf);
return 0;
}
delta_cache_size -= trg_entry->delta_size;
free(trg_entry->delta_data);
trg_entry->delta_data = NULL;
}
trg_entry->delta_data = 0;
trg_entry->delta = src_entry;
trg_entry->delta_size = delta_size;
trg_entry->depth = src_entry->depth + 1;
trg->depth = src->depth + 1;
if (delta_cacheable(src, trg, src_size, trg_size, delta_size)) {
trg_entry->delta_data = xrealloc(delta_buf, delta_size);
@@ -1400,9 +1420,23 @@ static unsigned int check_delta_limit(struct object_entry *me, unsigned int n)
return m;
}
static void free_unpacked(struct unpacked *n)
{
window_memory_usage -= sizeof_delta_index(n->index);
free_delta_index(n->index);
n->index = NULL;
if (n->data) {
free(n->data);
n->data = NULL;
window_memory_usage -= n->entry->size;
}
n->entry = NULL;
n->depth = 0;
}
static void find_deltas(struct object_entry **list, int window, int depth)
{
uint32_t i = nr_objects, idx = 0, processed = 0;
uint32_t i = nr_objects, idx = 0, count = 0, processed = 0;
unsigned int array_size = window * sizeof(struct unpacked);
struct unpacked *array;
int max_depth;
@@ -1437,12 +1471,17 @@ static void find_deltas(struct object_entry **list, int window, int depth)
if (entry->no_try_delta)
continue;
free_delta_index(n->index);
n->index = NULL;
free(n->data);
n->data = NULL;
free_unpacked(n);
n->entry = entry;
while (window_memory_limit &&
window_memory_usage > window_memory_limit &&
count > 1) {
uint32_t tail = (idx + window - count) % window;
free_unpacked(array + tail);
count--;
}
/*
* If the current object is at pack edge, take the depth the
* objects that depend on the current object into account
@@ -1472,11 +1511,13 @@ static void find_deltas(struct object_entry **list, int window, int depth)
* depth, leaving it in the window is pointless. we
* should evict it first.
*/
if (entry->delta && depth <= entry->depth)
if (entry->delta && depth <= n->depth)
continue;
next:
idx++;
if (count + 1 < window)
count++;
if (idx >= window)
idx = 0;
} while (i > 0);
@@ -1515,7 +1556,11 @@ static int git_pack_config(const char *k, const char *v)
window = git_config_int(k, v);
return 0;
}
if(!strcmp(k, "pack.depth")) {
if (!strcmp(k, "pack.windowmemory")) {
window_memory_limit = git_config_ulong(k, v);
return 0;
}
if (!strcmp(k, "pack.depth")) {
depth = git_config_int(k, v);
return 0;
}
@@ -1691,6 +1736,11 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
usage(pack_usage);
continue;
}
if (!prefixcmp(arg, "--window-memory=")) {
if (!git_parse_ulong(arg+16, &window_memory_limit))
usage(pack_usage);
continue;
}
if (!prefixcmp(arg, "--depth=")) {
char *end;
depth = strtoul(arg+8, &end, 0);

View File

@@ -1,3 +1,4 @@
#include "builtin.h"
#include "cache.h"
#include "refs.h"
#include "object.h"

View File

@@ -1,3 +1,4 @@
#include "builtin.h"
#include "cache.h"
#include "path-list.h"
#include "xdiff/xdiff.h"
@@ -12,6 +13,9 @@ static const char git_rerere_usage[] =
static int cutoff_noresolve = 15;
static int cutoff_resolve = 60;
/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
static int rerere_enabled = -1;
static char *merge_rr_path;
static const char *rr_path(const char *name, const char *file)
@@ -165,19 +169,16 @@ static int find_conflict(struct path_list *conflict)
int i;
if (read_cache() < 0)
return error("Could not read index");
for (i = 0; i + 2 < active_nr; i++) {
struct cache_entry *e1 = active_cache[i];
struct cache_entry *e2 = active_cache[i+1];
struct cache_entry *e3 = active_cache[i+2];
if (ce_stage(e1) == 1 &&
ce_stage(e2) == 2 &&
for (i = 0; i+1 < active_nr; i++) {
struct cache_entry *e2 = active_cache[i];
struct cache_entry *e3 = active_cache[i+1];
if (ce_stage(e2) == 2 &&
ce_stage(e3) == 3 &&
ce_same_name(e1, e2) && ce_same_name(e1, e3) &&
S_ISREG(ntohl(e1->ce_mode)) &&
ce_same_name(e2, e3) &&
S_ISREG(ntohl(e2->ce_mode)) &&
S_ISREG(ntohl(e3->ce_mode))) {
path_list_insert((const char *)e1->name, conflict);
i += 2;
path_list_insert((const char *)e2->name, conflict);
i++; /* skip over both #2 and #3 */
}
}
return 0;
@@ -282,8 +283,8 @@ static int diff_two(const char *file1, const char *label1,
printf("--- a/%s\n+++ b/%s\n", label1, label2);
fflush(stdout);
xpp.flags = XDF_NEED_MINIMAL;
memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 3;
xecfg.flags = 0;
ecb.outf = outf;
xdl_diff(&minus, &plus, &xpp, &xecfg, &ecb);
@@ -387,21 +388,41 @@ static int git_rerere_config(const char *var, const char *value)
cutoff_resolve = git_config_int(var, value);
else if (!strcmp(var, "gc.rerereunresolved"))
cutoff_noresolve = git_config_int(var, value);
else if (!strcmp(var, "rerere.enabled"))
rerere_enabled = git_config_bool(var, value);
else
return git_default_config(var, value);
return 0;
}
static int is_rerere_enabled(void)
{
struct stat st;
const char *rr_cache;
int rr_cache_exists;
if (!rerere_enabled)
return 0;
rr_cache = git_path("rr-cache");
rr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode);
if (rerere_enabled < 0)
return rr_cache_exists;
if (!rr_cache_exists &&
(mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache)))
die("Could not create directory %s", rr_cache);
return 1;
}
int cmd_rerere(int argc, const char **argv, const char *prefix)
{
struct path_list merge_rr = { NULL, 0, 0, 1 };
int i, fd = -1;
struct stat st;
if (stat(git_path("rr-cache"), &st) || !S_ISDIR(st.st_mode))
return 0;
git_config(git_rerere_config);
if (!is_rerere_enabled())
return 0;
merge_rr_path = xstrdup(git_path("rr-cache/MERGE_RR"));
fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1);
@@ -411,6 +432,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
return do_plain_rerere(&merge_rr, fd);
else if (!strcmp(argv[1], "clear")) {
for (i = 0; i < merge_rr.nr; i++) {
struct stat st;
const char *name = (const char *)merge_rr.items[i].util;
if (!stat(git_path("rr-cache/%s", name), &st) &&
S_ISDIR(st.st_mode) &&

View File

@@ -70,21 +70,9 @@ static void show_commit(struct commit *commit)
if (revs.parents) {
struct commit_list *parents = commit->parents;
while (parents) {
struct object *o = &(parents->item->object);
printf(" %s", sha1_to_hex(parents->item->object.sha1));
parents = parents->next;
if (o->flags & TMP_MARK)
continue;
printf(" %s", sha1_to_hex(o->sha1));
o->flags |= TMP_MARK;
}
/* TMP_MARK is a general purpose flag that can
* be used locally, but the user should clean
* things up after it is done with them.
*/
for (parents = commit->parents;
parents;
parents = parents->next)
parents->item->object.flags &= ~TMP_MARK;
}
if (revs.commit_format == CMIT_FMT_ONELINE)
putchar(' ');

View File

@@ -46,7 +46,7 @@ static int remove_file(const char *name)
return ret;
}
static int check_local_mod(unsigned char *head)
static int check_local_mod(unsigned char *head, int index_only)
{
/* items in list are already sorted in the cache order,
* so we could do this a lot more efficiently by using
@@ -65,6 +65,8 @@ static int check_local_mod(unsigned char *head)
const char *name = list.name[i];
unsigned char sha1[20];
unsigned mode;
int local_changes = 0;
int staged_changes = 0;
pos = cache_name_pos(name, strlen(name));
if (pos < 0)
@@ -87,14 +89,32 @@ static int check_local_mod(unsigned char *head)
continue;
}
if (ce_match_stat(ce, &st, 0))
errs = error("'%s' has local modifications "
"(hint: try -f)", ce->name);
local_changes = 1;
if (no_head
|| get_tree_entry(head, name, sha1, &mode)
|| ce->ce_mode != create_ce_mode(mode)
|| hashcmp(ce->sha1, sha1))
errs = error("'%s' has changes staged in the index "
"(hint: try -f)", name);
staged_changes = 1;
if (local_changes && staged_changes)
errs = error("'%s' has staged content different "
"from both the file and the HEAD\n"
"(use -f to force removal)", name);
else if (!index_only) {
/* It's not dangerous to git-rm --cached a
* file if the index matches the file or the
* HEAD, since it means the deleted content is
* still available somewhere.
*/
if (staged_changes)
errs = error("'%s' has changes staged in the index\n"
"(use --cached to keep the file, "
"or -f to force removal)", name);
if (local_changes)
errs = error("'%s' has local modifications\n"
"(use --cached to keep the file, "
"or -f to force removal)", name);
}
}
return errs;
}
@@ -192,7 +212,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
unsigned char sha1[20];
if (get_sha1("HEAD", sha1))
hashclr(sha1);
if (check_local_mod(sha1))
if (check_local_mod(sha1, index_only))
exit(1);
}

View File

@@ -1,3 +1,4 @@
#include "builtin.h"
#include "cache.h"
#include "wt-status.h"

View File

@@ -1,3 +1,4 @@
#include "builtin.h"
#include "cache.h"
#include "refs.h"
#include "object.h"

View File

@@ -2,12 +2,11 @@
#include "cache.h"
/*
* Remove trailing spaces from a line.
* Returns the length of a line, without trailing spaces.
*
* If the line ends with newline, it will be removed too.
* Returns the new length of the string.
*/
static int cleanup(char *line, int len)
static size_t cleanup(char *line, size_t len)
{
if (len) {
if (line[len - 1] == '\n')
@@ -19,7 +18,6 @@ static int cleanup(char *line, int len)
break;
len--;
}
line[len] = 0;
}
return len;
}
@@ -28,52 +26,67 @@ static int cleanup(char *line, int len)
* Remove empty lines from the beginning and end
* and also trailing spaces from every line.
*
* Note that the buffer will not be NUL-terminated.
*
* Turn multiple consecutive empty lines between paragraphs
* into just one empty line.
*
* If the input has only empty lines and spaces,
* no output will be produced.
*
* If last line has a newline at the end, it will be removed.
*
* Enable skip_comments to skip every line starting with "#".
*/
void stripspace(FILE *in, FILE *out, int skip_comments)
size_t stripspace(char *buffer, size_t length, int skip_comments)
{
int empties = -1;
int alloc = 1024;
char *line = xmalloc(alloc);
size_t i, j, len, newlen;
char *eol;
while (fgets(line, alloc, in)) {
int len = strlen(line);
for (i = j = 0; i < length; i += len, j += newlen) {
eol = memchr(buffer + i, '\n', length - i);
len = eol ? eol - (buffer + i) + 1 : length - i;
while (len == alloc - 1 && line[len - 1] != '\n') {
alloc = alloc_nr(alloc);
line = xrealloc(line, alloc);
fgets(line + len, alloc - len, in);
len += strlen(line + len);
}
if (skip_comments && line[0] == '#')
if (skip_comments && len && buffer[i] == '#') {
newlen = 0;
continue;
len = cleanup(line, len);
}
newlen = cleanup(buffer + i, len);
/* Not just an empty line? */
if (len) {
if (newlen) {
if (empties != -1)
buffer[j++] = '\n';
if (empties > 0)
fputc('\n', out);
buffer[j++] = '\n';
empties = 0;
fputs(line, out);
fputc('\n', out);
memmove(buffer + j, buffer + i, newlen);
continue;
}
if (empties < 0)
continue;
empties++;
}
free(line);
return j;
}
int cmd_stripspace(int argc, const char **argv, const char *prefix)
{
stripspace(stdin, stdout, 0);
char *buffer;
unsigned long size;
size = 1024;
buffer = xmalloc(size);
if (read_pipe(0, &buffer, &size))
die("could not read the input");
size = stripspace(buffer, size, 0);
write_or_die(1, buffer, size);
if (size)
putc('\n', stdout);
free(buffer);
return 0;
}

View File

@@ -7,7 +7,7 @@ extern const char git_version_string[];
extern const char git_usage_string[];
extern void help_unknown_cmd(const char *cmd);
extern void stripspace(FILE *in, FILE *out, int skip_comments);
extern size_t stripspace(char *buffer, size_t length, int skip_comments);
extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
extern void prune_packed_objects(int);

16
cache.h
View File

@@ -295,6 +295,7 @@ extern int delete_ref(const char *, const unsigned char *sha1);
/* Environment bits from configuration mechanism */
extern int trust_executable_bit;
extern int quote_path_fully;
extern int has_symlinks;
extern int assume_unchanged;
extern int prefer_symlink_refs;
@@ -409,9 +410,16 @@ extern void *read_object_with_reference(const unsigned char *sha1,
unsigned long *size,
unsigned char *sha1_ret);
enum date_mode { DATE_NORMAL = 0, DATE_RELATIVE, DATE_SHORT, DATE_LOCAL };
enum date_mode {
DATE_NORMAL = 0,
DATE_RELATIVE,
DATE_SHORT,
DATE_LOCAL,
DATE_ISO8601,
DATE_RFC2822
};
const char *show_date(unsigned long time, int timezone, enum date_mode mode);
const char *show_rfc2822_date(unsigned long time, int timezone);
int parse_date(const char *date, char *buf, int bufsize);
void datestamp(char *buf, int bufsize);
unsigned long approxidate(const char *);
@@ -521,7 +529,10 @@ typedef int (*config_fn_t)(const char *, const char *);
extern int git_default_config(const char *, const char *);
extern int git_config_from_file(config_fn_t fn, const char *);
extern int git_config(config_fn_t fn);
extern int git_parse_long(const char *, long *);
extern int git_parse_ulong(const char *, unsigned long *);
extern int git_config_int(const char *, const char *);
extern unsigned long git_config_ulong(const char *, const char *);
extern int git_config_bool(const char *, const char *);
extern int git_config_set(const char *, const char *);
extern int git_config_set_multivar(const char *, const char *, const char *, int);
@@ -547,6 +558,7 @@ extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char
/* pager.c */
extern void setup_pager(void);
extern char *pager_program;
extern int pager_in_use;
extern int pager_use_color;

View File

@@ -215,8 +215,7 @@ static void combine_diff(const unsigned char *parent, mmfile_t *result_file,
parent_file.ptr = grab_blob(parent, &sz);
parent_file.size = sz;
xpp.flags = XDF_NEED_MINIMAL;
xecfg.ctxlen = 0;
xecfg.flags = 0;
memset(&xecfg, 0, sizeof(xecfg));
ecb.outf = xdiff_outf;
ecb.priv = &state;
memset(&state, 0, sizeof(state));

View File

@@ -585,7 +585,7 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
break;
case CMIT_FMT_EMAIL:
ret += sprintf(buf + ret, "Date: %s\n",
show_rfc2822_date(time, tz));
show_date(time, tz, DATE_RFC2822));
break;
case CMIT_FMT_FULLER:
ret += sprintf(buf + ret, "%sDate: %s\n", what,
@@ -778,9 +778,10 @@ static void fill_person(struct interp *table, const char *msg, int len)
tz = -tz;
}
interp_set_entry(table, 2, show_date(date, tz, 0));
interp_set_entry(table, 3, show_rfc2822_date(date, tz));
interp_set_entry(table, 4, show_date(date, tz, 1));
interp_set_entry(table, 2, show_date(date, tz, DATE_NORMAL));
interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822));
interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE));
interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601));
}
static long format_commit_message(const struct commit *commit,
@@ -799,12 +800,14 @@ static long format_commit_message(const struct commit *commit,
{ "%aD" }, /* author date, RFC2822 style */
{ "%ar" }, /* author date, relative */
{ "%at" }, /* author date, UNIX timestamp */
{ "%ai" }, /* author date, ISO 8601 */
{ "%cn" }, /* committer name */
{ "%ce" }, /* committer email */
{ "%cd" }, /* committer date */
{ "%cD" }, /* committer date, RFC2822 style */
{ "%cr" }, /* committer date, relative */
{ "%ct" }, /* committer date, UNIX timestamp */
{ "%ci" }, /* committer date, ISO 8601 */
{ "%e" }, /* encoding */
{ "%s" }, /* subject */
{ "%b" }, /* body */
@@ -821,10 +824,11 @@ static long format_commit_message(const struct commit *commit,
IPARENTS, IPARENTS_ABBREV,
IAUTHOR_NAME, IAUTHOR_EMAIL,
IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE,
IAUTHOR_TIMESTAMP,
IAUTHOR_TIMESTAMP, IAUTHOR_ISO8601,
ICOMMITTER_NAME, ICOMMITTER_EMAIL,
ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822,
ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP,
ICOMMITTER_ISO8601,
IENCODING,
ISUBJECT,
IBODY,

View File

@@ -234,21 +234,55 @@ static int git_parse_file(config_fn_t fn)
die("bad config file line %d in %s", config_linenr, config_file_name);
}
int git_config_int(const char *name, const char *value)
static unsigned long get_unit_factor(const char *end)
{
if (!*end)
return 1;
else if (!strcasecmp(end, "k"))
return 1024;
else if (!strcasecmp(end, "m"))
return 1024 * 1024;
else if (!strcasecmp(end, "g"))
return 1024 * 1024 * 1024;
die("unknown unit: '%s'", end);
}
int git_parse_long(const char *value, long *ret)
{
if (value && *value) {
char *end;
int val = strtol(value, &end, 0);
if (!*end)
return val;
if (!strcasecmp(end, "k"))
return val * 1024;
if (!strcasecmp(end, "m"))
return val * 1024 * 1024;
if (!strcasecmp(end, "g"))
return val * 1024 * 1024 * 1024;
long val = strtol(value, &end, 0);
*ret = val * get_unit_factor(end);
return 1;
}
die("bad config value for '%s' in %s", name, config_file_name);
return 0;
}
int git_parse_ulong(const char *value, unsigned long *ret)
{
if (value && *value) {
char *end;
unsigned long val = strtoul(value, &end, 0);
*ret = val * get_unit_factor(end);
return 1;
}
return 0;
}
int git_config_int(const char *name, const char *value)
{
long ret;
if (!git_parse_long(value, &ret))
die("bad config value for '%s' in %s", name, config_file_name);
return ret;
}
unsigned long git_config_ulong(const char *name, const char *value)
{
unsigned long ret;
if (!git_parse_ulong(value, &ret))
die("bad config value for '%s' in %s", name, config_file_name);
return ret;
}
int git_config_bool(const char *name, const char *value)
@@ -272,6 +306,11 @@ int git_default_config(const char *var, const char *value)
return 0;
}
if (!strcmp(var, "core.quotepath")) {
quote_path_fully = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "core.symlinks")) {
has_symlinks = git_config_bool(var, value);
return 0;
@@ -383,6 +422,11 @@ int git_default_config(const char *var, const char *value)
return 0;
}
if (!strcmp(var, "core.pager")) {
pager_program = xstrdup(value);
return 0;
}
/* Add other config variables here and to Documentation/config.txt. */
return 0;
}

View File

@@ -73,7 +73,7 @@ struct ref **get_remote_heads(int in, struct ref **list,
continue;
if (nr_match && !path_match(name, nr_match, match))
continue;
ref = xcalloc(1, sizeof(*ref) + len - 40);
ref = alloc_ref(len - 40);
hashcpy(ref->old_sha1, old_sha1);
memcpy(ref->name, buffer + 41, len - 40);
*list = ref;

View File

@@ -7,6 +7,7 @@ INSTALL ?= install
INSTALL_ELC = $(INSTALL) -m 644
prefix ?= $(HOME)
emacsdir = $(prefix)/share/emacs/site-lisp
RM ?= rm -f
all: $(ELC)
@@ -17,4 +18,4 @@ install: all
%.elc: %.el
$(EMACS) -batch -f batch-byte-compile $<
clean:; rm -f $(ELC)
clean:; $(RM) $(ELC)

View File

@@ -681,8 +681,7 @@ and returns the process output as a string."
(condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
(with-current-buffer buffer (erase-buffer))
(git-set-files-state files 'uptodate)
(when (file-directory-p ".git/rr-cache")
(git-run-command nil nil "rerere"))
(git-run-command nil nil "rerere")
(git-refresh-files)
(git-refresh-ewoc-hf git-status)
(message "Committed %s." commit)

View File

@@ -81,6 +81,71 @@
(match-string 2 str)
str)))
(defun vc-git-symbolic-commit (commit)
"Translate COMMIT string into symbolic form.
Returns nil if not possible."
(and commit
(with-temp-buffer
(and
(zerop
(call-process "git" nil '(t nil) nil "name-rev"
"--name-only" "--tags"
commit))
(goto-char (point-min))
(= (forward-line 2) 1)
(bolp)
(buffer-substring-no-properties (point-min) (1- (point-max)))))))
(defun vc-git-previous-version (file rev)
"git-specific version of `vc-previous-version'."
(let ((default-directory (file-name-directory (expand-file-name file)))
(file (file-name-nondirectory file)))
(vc-git-symbolic-commit
(with-temp-buffer
(and
(zerop
(call-process "git" nil '(t nil) nil "rev-list"
"-2" rev "--" file))
(goto-char (point-max))
(bolp)
(zerop (forward-line -1))
(not (bobp))
(buffer-substring-no-properties
(point)
(1- (point-max))))))))
(defun vc-git-next-version (file rev)
"git-specific version of `vc-next-version'."
(let* ((default-directory (file-name-directory
(expand-file-name file)))
(file (file-name-nondirectory file))
(current-rev
(with-temp-buffer
(and
(zerop
(call-process "git" nil '(t nil) nil "rev-list"
"-1" rev "--" file))
(goto-char (point-max))
(bolp)
(zerop (forward-line -1))
(bobp)
(buffer-substring-no-properties
(point)
(1- (point-max)))))))
(and current-rev
(vc-git-symbolic-commit
(with-temp-buffer
(and
(zerop
(call-process "git" nil '(t nil) nil "rev-list"
"HEAD" "--" file))
(goto-char (point-min))
(search-forward current-rev nil t)
(zerop (forward-line -1))
(buffer-substring-no-properties
(point)
(progn (forward-line 1) (1- (point))))))))))
(defun vc-git-revert (file &optional contents-done)
"Revert FILE to the version stored in the git repository."
(if contents-done

View File

@@ -30,8 +30,8 @@ notbare|"")
esac
test "true" != "$pack_refs" ||
git-pack-refs --prune &&
git-reflog expire --all &&
git pack-refs --prune &&
git reflog expire --all &&
git-repack -a -d -l &&
$no_prune git-prune &&
git-rerere gc || exit
$no_prune git prune &&
git rerere gc || exit

View File

@@ -17,8 +17,8 @@ dropheads() {
"$GIT_DIR/LAST_MERGE" || exit 1
}
head=$(git-rev-parse --verify "$1"^0) &&
merge=$(git-rev-parse --verify "$2"^0) &&
head=$(git rev-parse --verify "$1"^0) &&
merge=$(git rev-parse --verify "$2"^0) &&
merge_name="$2" &&
merge_msg="$3" || usage
@@ -34,7 +34,7 @@ dropheads
echo $head > "$GIT_DIR"/ORIG_HEAD
echo $merge > "$GIT_DIR"/LAST_MERGE
common=$(git-merge-base $head $merge)
common=$(git merge-base $head $merge)
if [ -z "$common" ]; then
die "Unable to find common commit between" $merge $head
fi
@@ -46,11 +46,11 @@ case "$common" in
exit 0
;;
"$head")
echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $merge)"
git-read-tree -u -m $head $merge || exit 1
git-update-ref -m "resolve $merge_name: Fast forward" \
echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $merge)"
git read-tree -u -m $head $merge || exit 1
git update-ref -m "resolve $merge_name: Fast forward" \
HEAD "$merge" "$head"
git-diff-tree -p $head $merge | git-apply --stat
git diff-tree -p $head $merge | git apply --stat
dropheads
exit 0
;;
@@ -62,7 +62,7 @@ git var GIT_COMMITTER_IDENT >/dev/null || exit
# Find an optimum merge base if there are more than one candidates.
LF='
'
common=$(git-merge-base -a $head $merge)
common=$(git merge-base -a $head $merge)
case "$common" in
?*"$LF"?*)
echo "Trying to find the optimum merge base."
@@ -72,10 +72,10 @@ case "$common" in
for c in $common
do
rm -f $G
GIT_INDEX_FILE=$G git-read-tree -m $c $head $merge \
GIT_INDEX_FILE=$G git read-tree -m $c $head $merge \
2>/dev/null || continue
# Count the paths that are unmerged.
cnt=`GIT_INDEX_FILE=$G git-ls-files --unmerged | wc -l`
cnt=`GIT_INDEX_FILE=$G git ls-files --unmerged | wc -l`
if test $best_cnt -le 0 -o $cnt -le $best_cnt
then
best=$c
@@ -92,9 +92,9 @@ case "$common" in
esac
echo "Trying to merge $merge into $head using $common."
git-update-index --refresh 2>/dev/null
git-read-tree -u -m $common $head $merge || exit 1
result_tree=$(git-write-tree 2> /dev/null)
git update-index --refresh 2>/dev/null
git read-tree -u -m $common $head $merge || exit 1
result_tree=$(git write-tree 2> /dev/null)
if [ $? -ne 0 ]; then
echo "Simple merge failed, trying Automatic merge"
git-merge-index -o git-merge-one-file -a
@@ -102,11 +102,11 @@ if [ $? -ne 0 ]; then
echo $merge > "$GIT_DIR"/MERGE_HEAD
die "Automatic merge failed, fix up by hand"
fi
result_tree=$(git-write-tree) || exit 1
result_tree=$(git write-tree) || exit 1
fi
result_commit=$(echo "$merge_msg" | git-commit-tree $result_tree -p $head -p $merge)
result_commit=$(echo "$merge_msg" | git commit-tree $result_tree -p $head -p $merge)
echo "Committed merge $result_commit"
git-update-ref -m "resolve $merge_name: In-index merge" \
git update-ref -m "resolve $merge_name: In-index merge" \
HEAD "$result_commit" "$head"
git-diff-tree -p $head $result_commit | git-apply --stat
git diff-tree -p $head $result_commit | git apply --stat
dropheads

View File

@@ -717,7 +717,11 @@ class P4Sync(Command):
# POSIX says it's 4096 bytes, default for Linux seems to be 130 K.
# and all OS from the table below seems to be higher than POSIX.
# See http://www.in-ulm.de/~mascheck/various/argmax/
argmax = min(4000, os.sysconf('SC_ARG_MAX'))
if (self.isWindows):
argmax = 2000
else:
argmax = min(4000, os.sysconf('SC_ARG_MAX'))
chunk = ''
filedata = []
for i in xrange(len(files)):

1
contrib/p4import/README Normal file
View File

@@ -0,0 +1 @@
Please see contrib/fast-import/git-p4 for a better Perforce importer.

View File

@@ -26,8 +26,8 @@ if [ -d "$GIT_DIR"/remotes ]; then
mv "$GIT_DIR"/remotes "$GIT_DIR"/remotes.old
fi ;;
*)
echo "git-config $key "$value" $regex"
git-config $key "$value" $regex || error=1 ;;
echo "git config $key "$value" $regex"
git config $key "$value" $regex || error=1 ;;
esac
done
fi

26
contrib/stats/git-common-hash Executable file
View File

@@ -0,0 +1,26 @@
#!/bin/sh
# This script displays the distribution of longest common hash prefixes.
# This can be used to determine the minimum prefix length to use
# for object names to be unique.
git rev-list --objects --all | sort | perl -lne '
substr($_, 40) = "";
# uncomment next line for a distribution of bits instead of hex chars
# $_ = unpack("B*",pack("H*",$_));
if (defined $p) {
($p ^ $_) =~ /^(\0*)/;
$common = length $1;
if (defined $pcommon) {
$count[$pcommon > $common ? $pcommon : $common]++;
} else {
$count[$common]++; # first item
}
}
$p = $_;
$pcommon = $common;
END {
$count[$common]++; # last item
print "$_: $count[$_]" for 0..$#count;
}
'

38
contrib/stats/mailmap.pl Executable file
View File

@@ -0,0 +1,38 @@
#!/usr/bin/perl -w
my %mailmap = ();
open I, "<", ".mailmap";
while (<I>) {
chomp;
next if /^#/;
if (my ($author, $mail) = /^(.*?)\s+<(.+)>$/) {
$mailmap{$mail} = $author;
}
}
close I;
my %mail2author = ();
open I, "git log --pretty='format:%ae %an' |";
while (<I>) {
chomp;
my ($mail, $author) = split(/\t/, $_);
next if exists $mailmap{$mail};
$mail2author{$mail} ||= {};
$mail2author{$mail}{$author} ||= 0;
$mail2author{$mail}{$author}++;
}
close I;
while (my ($mail, $authorcount) = each %mail2author) {
# %$authorcount is ($author => $count);
# sort and show the names from the most frequent ones.
my @names = (map { $_->[0] }
sort { $b->[1] <=> $a->[1] }
map { [$_, $authorcount->{$_}] }
keys %$authorcount);
if (1 < @names) {
for (@names) {
print "$_ <$mail>\n";
}
}
}

212
contrib/stats/packinfo.pl Executable file
View File

@@ -0,0 +1,212 @@
#!/usr/bin/perl
#
# This tool will print vaguely pretty information about a pack. It
# expects the output of "git-verify-pack -v" as input on stdin.
#
# $ git-verify-pack -v | packinfo.pl
#
# This prints some full-pack statistics; currently "all sizes", "all
# path sizes", "tree sizes", "tree path sizes", and "depths".
#
# * "all sizes" stats are across every object size in the file;
# full sizes for base objects, and delta size for deltas.
# * "all path sizes" stats are across all object's "path sizes".
# A path size is the sum of the size of the delta chain, including the
# base object. In other words, it's how many bytes need be read to
# reassemble the file from deltas.
# * "tree sizes" are object sizes grouped into delta trees.
# * "tree path sizes" are path sizes grouped into delta trees.
# * "depths" should be obvious.
#
# When run as:
#
# $ git-verify-pack -v | packinfo.pl -tree
#
# the trees of objects are output along with the stats. This looks
# like:
#
# 0 commit 031321c6... 803 803
#
# 0 blob 03156f21... 1767 1767
# 1 blob f52a9d7f... 10 1777
# 2 blob a8cc5739... 51 1828
# 3 blob 660e90b1... 15 1843
# 4 blob 0cb8e3bb... 33 1876
# 2 blob e48607f0... 311 2088
# size: count 6 total 2187 min 10 max 1767 mean 364.50 median 51 std_dev 635.85
# path size: count 6 total 11179 min 1767 max 2088 mean 1863.17 median 1843 std_dev 107.26
#
# The first number after the sha1 is the object size, the second
# number is the path size. The statistics are across all objects in
# the previous delta tree. Obviously they are omitted for trees of
# one object.
#
# When run as:
#
# $ git-verify-pack -v | packinfo.pl -tree -filenames
#
# it adds filenames to the tree. Getting this information is slow:
#
# 0 blob 03156f21... 1767 1767 Documentation/git-lost-found.txt @ tags/v1.2.0~142
# 1 blob f52a9d7f... 10 1777 Documentation/git-lost-found.txt @ tags/v1.5.0-rc1~74
# 2 blob a8cc5739... 51 1828 Documentation/git-lost+found.txt @ tags/v0.99.9h^0
# 3 blob 660e90b1... 15 1843 Documentation/git-lost+found.txt @ master~3222^2~2
# 4 blob 0cb8e3bb... 33 1876 Documentation/git-lost+found.txt @ master~3222^2~3
# 2 blob e48607f0... 311 2088 Documentation/git-lost-found.txt @ tags/v1.5.2-rc3~4
# size: count 6 total 2187 min 10 max 1767 mean 364.50 median 51 std_dev 635.85
# path size: count 6 total 11179 min 1767 max 2088 mean 1863.17 median 1843 std_dev 107.26
#
# When run as:
#
# $ git-verify-pack -v | packinfo.pl -dump
#
# it prints out "sha1 size pathsize depth" for each sha1 in lexical
# order.
#
# 000079a2eaef17b7eae70e1f0f635557ea67b644 30 472 7
# 00013cafe6980411aa6fdd940784917b5ff50f0a 44 1542 4
# 000182eacf99cde27d5916aa415921924b82972c 499 499 0
# ...
#
# This is handy for comparing two packs. Adding "-filenames" will add
# filenames, as per "-tree -filenames" above.
use strict;
use Getopt::Long;
my $filenames = 0;
my $tree = 0;
my $dump = 0;
GetOptions("tree" => \$tree,
"filenames" => \$filenames,
"dump" => \$dump);
my %parents;
my %children;
my %sizes;
my @roots;
my %paths;
my %types;
my @commits;
my %names;
my %depths;
my @depths;
while (<STDIN>) {
my ($sha1, $type, $size, $offset, $depth, $parent) = split(/\s+/, $_);
next unless ($sha1 =~ /^[0-9a-f]{40}$/);
$depths{$sha1} = $depth || 0;
push(@depths, $depth || 0);
push(@commits, $sha1) if ($type eq 'commit');
push(@roots, $sha1) unless $parent;
$parents{$sha1} = $parent;
$types{$sha1} = $type;
push(@{$children{$parent}}, $sha1);
$sizes{$sha1} = $size;
}
if ($filenames && ($tree || $dump)) {
open(NAMES, "git-name-rev --all|");
while (<NAMES>) {
if (/^(\S+)\s+(.*)$/) {
my ($sha1, $name) = ($1, $2);
$names{$sha1} = $name;
}
}
close NAMES;
for my $commit (@commits) {
my $name = $names{$commit};
open(TREE, "git-ls-tree -t -r $commit|");
print STDERR "Plumbing tree $name\n";
while (<TREE>) {
if (/^(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) {
my ($mode, $type, $sha1, $path) = ($1, $2, $3, $4);
$paths{$sha1} = "$path @ $name";
}
}
close TREE;
}
}
sub stats {
my @data = sort {$a <=> $b} @_;
my $min = $data[0];
my $max = $data[$#data];
my $total = 0;
my $count = scalar @data;
for my $datum (@data) {
$total += $datum;
}
my $mean = $total / $count;
my $median = $data[int(@data / 2)];
my $diff_sum = 0;
for my $datum (@data) {
$diff_sum += ($datum - $mean)**2;
}
my $std_dev = sqrt($diff_sum / $count);
return ($count, $total, $min, $max, $mean, $median, $std_dev);
}
sub print_stats {
my $name = shift;
my ($count, $total, $min, $max, $mean, $median, $std_dev) = stats(@_);
printf("%s: count %s total %s min %s max %s mean %.2f median %s std_dev %.2f\n",
$name, $count, $total, $min, $max, $mean, $median, $std_dev);
}
my @sizes;
my @path_sizes;
my @all_sizes;
my @all_path_sizes;
my %path_sizes;
sub dig {
my ($sha1, $depth, $path_size) = @_;
$path_size += $sizes{$sha1};
push(@sizes, $sizes{$sha1});
push(@all_sizes, $sizes{$sha1});
push(@path_sizes, $path_size);
push(@all_path_sizes, $path_size);
$path_sizes{$sha1} = $path_size;
if ($tree) {
printf("%3d%s %6s %s %8d %8d %s\n",
$depth, (" " x $depth), $types{$sha1},
$sha1, $sizes{$sha1}, $path_size, $paths{$sha1});
}
for my $child (@{$children{$sha1}}) {
dig($child, $depth + 1, $path_size);
}
}
my @tree_sizes;
my @tree_path_sizes;
for my $root (@roots) {
undef @sizes;
undef @path_sizes;
dig($root, 0, 0);
my ($aa, $sz_total) = stats(@sizes);
my ($bb, $psz_total) = stats(@path_sizes);
push(@tree_sizes, $sz_total);
push(@tree_path_sizes, $psz_total);
if ($tree) {
if (@sizes > 1) {
print_stats(" size", @sizes);
print_stats("path size", @path_sizes);
}
print "\n";
}
}
if ($dump) {
for my $sha1 (sort keys %sizes) {
print "$sha1 $sizes{$sha1} $path_sizes{$sha1} $depths{$sha1} $paths{$sha1}\n";
}
} else {
print_stats(" all sizes", @all_sizes);
print_stats(" all path sizes", @all_path_sizes);
print_stats(" tree sizes", @tree_sizes);
print_stats("tree path sizes", @tree_path_sizes);
print_stats(" depths", @depths);
}

27
date.c
View File

@@ -137,6 +137,18 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
if (mode == DATE_SHORT)
sprintf(timebuf, "%04d-%02d-%02d", tm->tm_year + 1900,
tm->tm_mon + 1, tm->tm_mday);
else if (mode == DATE_ISO8601)
sprintf(timebuf, "%04d-%02d-%02d %02d:%02d:%02d %+05d",
tm->tm_year + 1900,
tm->tm_mon + 1,
tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec,
tz);
else if (mode == DATE_RFC2822)
sprintf(timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
weekday_names[tm->tm_wday], tm->tm_mday,
month_names[tm->tm_mon], tm->tm_year + 1900,
tm->tm_hour, tm->tm_min, tm->tm_sec, tz);
else
sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d",
weekday_names[tm->tm_wday],
@@ -149,21 +161,6 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
return timebuf;
}
const char *show_rfc2822_date(unsigned long time, int tz)
{
struct tm *tm;
static char timebuf[200];
tm = time_to_tm(time, tz);
if (!tm)
return NULL;
sprintf(timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
weekday_names[tm->tm_wday], tm->tm_mday,
month_names[tm->tm_mon], tm->tm_year + 1900,
tm->tm_hour, tm->tm_min, tm->tm_sec, tz);
return timebuf;
}
/*
* Check these. And note how it doesn't do the summer-time conversion.
*

View File

@@ -23,6 +23,13 @@ create_delta_index(const void *buf, unsigned long bufsize);
*/
extern void free_delta_index(struct delta_index *index);
/*
* sizeof_delta_index: returns memory usage of delta index
*
* Given pointer must be what create_delta_index() returned, or NULL.
*/
extern unsigned long sizeof_delta_index(struct delta_index *index);
/*
* create_delta: create a delta from given index for the given buffer
*

View File

@@ -119,6 +119,7 @@ struct index_entry {
};
struct delta_index {
unsigned long memsize;
const void *src_buf;
unsigned long src_size;
unsigned int hash_mask;
@@ -159,6 +160,7 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
mem = hash + hsize;
entry = mem;
index->memsize = memsize;
index->src_buf = buf;
index->src_size = bufsize;
index->hash_mask = hmask;
@@ -228,6 +230,14 @@ void free_delta_index(struct delta_index *index)
free(index);
}
unsigned long sizeof_delta_index(struct delta_index *index)
{
if (index)
return index->memsize;
else
return 0;
}
/*
* The maximum size for any opcode sequence, including the initial header
* plus Rabin window plus biggest copy.

View File

@@ -24,7 +24,7 @@ static int read_directory(const char *path, struct path_list *list)
while ((e = readdir(dir)))
if (strcmp(".", e->d_name) && strcmp("..", e->d_name))
path_list_insert(xstrdup(e->d_name), list);
path_list_insert(e->d_name, list);
closedir(dir);
return 0;

220
diff.c
View File

@@ -57,6 +57,14 @@ static struct ll_diff_driver {
char *cmd;
} *user_diff, **user_diff_tail;
static void read_config_if_needed(void)
{
if (!user_diff_tail) {
user_diff_tail = &user_diff;
git_config(git_diff_ui_config);
}
}
/*
* Currently there is only "diff.<drivername>.command" variable;
* because there are "diff.color.<slot>" variables, we are parsing
@@ -94,6 +102,45 @@ static int parse_lldiff_command(const char *var, const char *ep, const char *val
return 0;
}
/*
* 'diff.<what>.funcname' attribute can be specified in the configuration
* to define a customized regexp to find the beginning of a function to
* be used for hunk header lines of "diff -p" style output.
*/
static struct funcname_pattern {
char *name;
char *pattern;
struct funcname_pattern *next;
} *funcname_pattern_list;
static int parse_funcname_pattern(const char *var, const char *ep, const char *value)
{
const char *name;
int namelen;
struct funcname_pattern *pp;
name = var + 5; /* "diff." */
namelen = ep - name;
for (pp = funcname_pattern_list; pp; pp = pp->next)
if (!strncmp(pp->name, name, namelen) && !pp->name[namelen])
break;
if (!pp) {
char *namebuf;
pp = xcalloc(1, sizeof(*pp));
namebuf = xmalloc(namelen + 1);
memcpy(namebuf, name, namelen);
namebuf[namelen] = 0;
pp->name = namebuf;
pp->next = funcname_pattern_list;
funcname_pattern_list = pp;
}
if (pp->pattern)
free(pp->pattern);
pp->pattern = xstrdup(value);
return 0;
}
/*
* These are to give UI layer defaults.
* The core-level commands such as git-diff-files should
@@ -123,8 +170,12 @@ int git_diff_ui_config(const char *var, const char *value)
if (!prefixcmp(var, "diff.")) {
const char *ep = strrchr(var, '.');
if (ep != var + 4 && !strcmp(ep, ".command"))
return parse_lldiff_command(var, ep, value);
if (ep != var + 4) {
if (!strcmp(ep, ".command"))
return parse_lldiff_command(var, ep, value);
if (!strcmp(ep, ".funcname"))
return parse_funcname_pattern(var, ep, value);
}
}
if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
int slot = parse_diff_color_slot(var, 11);
@@ -391,6 +442,7 @@ static void diff_words_show(struct diff_words_data *diff_words)
mmfile_t minus, plus;
int i;
memset(&xecfg, 0, sizeof(xecfg));
minus.size = diff_words->minus.text.size;
minus.ptr = xmalloc(minus.size);
memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size);
@@ -409,7 +461,6 @@ static void diff_words_show(struct diff_words_data *diff_words)
xpp.flags = XDF_NEED_MINIMAL;
xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
xecfg.flags = 0;
ecb.outf = xdiff_outf;
ecb.priv = diff_words;
diff_words->xm.consume = fn_out_diff_words_aux;
@@ -1103,30 +1154,111 @@ static void setup_diff_attr_check(struct git_attr_check *check)
{
static struct git_attr *attr_diff;
if (!attr_diff)
if (!attr_diff) {
attr_diff = git_attr("diff", 4);
check->attr = attr_diff;
}
check[0].attr = attr_diff;
}
static int file_is_binary(struct diff_filespec *one)
static void diff_filespec_check_attr(struct diff_filespec *one)
{
struct git_attr_check attr_diff_check;
int check_from_data = 0;
if (one->checked_attr)
return;
setup_diff_attr_check(&attr_diff_check);
one->is_binary = 0;
one->funcname_pattern_ident = NULL;
if (!git_checkattr(one->path, 1, &attr_diff_check)) {
const char *value = attr_diff_check.value;
const char *value;
/* binaryness */
value = attr_diff_check.value;
if (ATTR_TRUE(value))
return 0;
;
else if (ATTR_FALSE(value))
return 1;
one->is_binary = 1;
else
check_from_data = 1;
/* funcname pattern ident */
if (ATTR_TRUE(value) || ATTR_FALSE(value) || ATTR_UNSET(value))
;
else
one->funcname_pattern_ident = value;
}
if (!one->data) {
if (!DIFF_FILE_VALID(one))
return 0;
diff_populate_filespec(one, 0);
if (check_from_data) {
if (!one->data && DIFF_FILE_VALID(one))
diff_populate_filespec(one, 0);
if (one->data)
one->is_binary = buffer_is_binary(one->data, one->size);
}
return buffer_is_binary(one->data, one->size);
}
int diff_filespec_is_binary(struct diff_filespec *one)
{
diff_filespec_check_attr(one);
return one->is_binary;
}
static const char *funcname_pattern(const char *ident)
{
struct funcname_pattern *pp;
read_config_if_needed();
for (pp = funcname_pattern_list; pp; pp = pp->next)
if (!strcmp(ident, pp->name))
return pp->pattern;
return NULL;
}
static struct builtin_funcname_pattern {
const char *name;
const char *pattern;
} builtin_funcname_pattern[] = {
{ "java", "!^[ ]*\\(catch\\|do\\|for\\|if\\|instanceof\\|"
"new\\|return\\|switch\\|throw\\|while\\)\n"
"^[ ]*\\(\\([ ]*"
"[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}"
"[ ]*([^;]*$\\)" },
{ "tex", "^\\(\\\\\\(sub\\)*section{.*\\)$" },
};
static const char *diff_funcname_pattern(struct diff_filespec *one)
{
const char *ident, *pattern;
int i;
diff_filespec_check_attr(one);
ident = one->funcname_pattern_ident;
if (!ident)
/*
* If the config file has "funcname.default" defined, that
* regexp is used; otherwise NULL is returned and xemit uses
* the built-in default.
*/
return funcname_pattern("default");
/* Look up custom "funcname.$ident" regexp from config. */
pattern = funcname_pattern(ident);
if (pattern)
return pattern;
/*
* And define built-in fallback patterns here. Note that
* these can be overriden by the user's config settings.
*/
for (i = 0; i < ARRAY_SIZE(builtin_funcname_pattern); i++)
if (!strcmp(ident, builtin_funcname_pattern[i].name))
return builtin_funcname_pattern[i].pattern;
return NULL;
}
static void builtin_diff(const char *name_a,
@@ -1183,7 +1315,8 @@ static void builtin_diff(const char *name_a,
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
if (!o->text && (file_is_binary(one) || file_is_binary(two))) {
if (!o->text &&
(diff_filespec_is_binary(one) || diff_filespec_is_binary(two))) {
/* Quite common confusing case */
if (mf1.size == mf2.size &&
!memcmp(mf1.ptr, mf2.ptr, mf1.size))
@@ -1202,7 +1335,13 @@ static void builtin_diff(const char *name_a,
xdemitconf_t xecfg;
xdemitcb_t ecb;
struct emit_callback ecbdata;
const char *funcname_pattern;
funcname_pattern = diff_funcname_pattern(one);
if (!funcname_pattern)
funcname_pattern = diff_funcname_pattern(two);
memset(&xecfg, 0, sizeof(xecfg));
memset(&ecbdata, 0, sizeof(ecbdata));
ecbdata.label_path = lbl;
ecbdata.color_diff = o->color_diff;
@@ -1210,6 +1349,8 @@ static void builtin_diff(const char *name_a,
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
xecfg.ctxlen = o->context;
xecfg.flags = XDL_EMIT_FUNCNAMES;
if (funcname_pattern)
xdiff_set_find_func(&xecfg, funcname_pattern);
if (!diffopts)
;
else if (!prefixcmp(diffopts, "--unified="))
@@ -1261,7 +1402,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
if (file_is_binary(one) || file_is_binary(two)) {
if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
data->is_binary = 1;
data->added = mf2.size;
data->deleted = mf1.size;
@@ -1271,9 +1412,8 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
xdemitconf_t xecfg;
xdemitcb_t ecb;
memset(&xecfg, 0, sizeof(xecfg));
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
xecfg.ctxlen = 0;
xecfg.flags = 0;
ecb.outf = xdiff_outf;
ecb.priv = diffstat;
xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
@@ -1303,7 +1443,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
if (file_is_binary(two))
if (diff_filespec_is_binary(two))
goto free_and_return;
else {
/* Crazy xdl interfaces.. */
@@ -1311,9 +1451,8 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
xdemitconf_t xecfg;
xdemitcb_t ecb;
memset(&xecfg, 0, sizeof(xecfg));
xpp.flags = XDF_NEED_MINIMAL;
xecfg.ctxlen = 0;
xecfg.flags = 0;
ecb.outf = xdiff_outf;
ecb.priv = &data;
xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
@@ -1748,10 +1887,7 @@ static const char *external_diff_attr(const char *name)
!ATTR_UNSET(value)) {
struct ll_diff_driver *drv;
if (!user_diff_tail) {
user_diff_tail = &user_diff;
git_config(git_diff_ui_config);
}
read_config_if_needed();
for (drv = user_diff; drv; drv = drv->next)
if (!strcmp(drv->name, value))
return drv->cmd;
@@ -1808,6 +1944,11 @@ static void diff_fill_sha1_info(struct diff_filespec *one)
hashclr(one->sha1);
}
static int similarity_index(struct diff_filepair *p)
{
return p->score * 100 / MAX_SCORE;
}
static void run_diff(struct diff_filepair *p, struct diff_options *o)
{
const char *pgm = external_diff();
@@ -1842,23 +1983,20 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
"similarity index %d%%\n"
"copy from %s\n"
"copy to %s\n",
(int)(0.5 + p->score * 100.0/MAX_SCORE),
name_munged, other_munged);
similarity_index(p), name_munged, other_munged);
break;
case DIFF_STATUS_RENAMED:
len += snprintf(msg + len, sizeof(msg) - len,
"similarity index %d%%\n"
"rename from %s\n"
"rename to %s\n",
(int)(0.5 + p->score * 100.0/MAX_SCORE),
name_munged, other_munged);
similarity_index(p), name_munged, other_munged);
break;
case DIFF_STATUS_MODIFIED:
if (p->score) {
len += snprintf(msg + len, sizeof(msg) - len,
"dissimilarity index %d%%\n",
(int)(0.5 + p->score *
100.0/MAX_SCORE));
similarity_index(p));
complete_rewrite = 1;
break;
}
@@ -1873,8 +2011,8 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
if (o->binary) {
mmfile_t mf;
if ((!fill_mmfile(&mf, one) && file_is_binary(one)) ||
(!fill_mmfile(&mf, two) && file_is_binary(two)))
if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
(!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
abbrev = 40;
}
len += snprintf(msg + len, sizeof(msg) - len,
@@ -2234,6 +2372,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
options->exit_with_status = 1;
else if (!strcmp(arg, "--quiet"))
options->quiet = 1;
else if (!strcmp(arg, "--ext-diff"))
options->allow_external = 1;
else if (!strcmp(arg, "--no-ext-diff"))
options->allow_external = 0;
else
return 0;
return 1;
@@ -2382,8 +2524,7 @@ static void diff_flush_raw(struct diff_filepair *p,
}
if (p->score)
sprintf(status, "%c%03d", p->status,
(int)(0.5 + p->score * 100.0/MAX_SCORE));
sprintf(status, "%c%03d", p->status, similarity_index(p));
else {
status[0] = p->status;
status[1] = 0;
@@ -2666,8 +2807,7 @@ static void show_rename_copy(const char *renamecopy, struct diff_filepair *p)
{
char *names = pprint_rename(p->one->path, p->two->path);
printf(" %s %s (%d%%)\n", renamecopy, names,
(int)(0.5 + p->score * 100.0/MAX_SCORE));
printf(" %s %s (%d%%)\n", renamecopy, names, similarity_index(p));
free(names);
show_mode_change(p, 0);
}
@@ -2691,7 +2831,7 @@ static void diff_summary(struct diff_filepair *p)
if (p->score) {
char *name = quote_one(p->two->path);
printf(" rewrite %s (%d%%)\n", name,
(int)(0.5 + p->score * 100.0/MAX_SCORE));
similarity_index(p));
free(name);
show_mode_change(p, 0);
} else show_mode_change(p, 1);
@@ -2755,6 +2895,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
struct diff_filepair *p = q->queue[i];
int len1, len2;
memset(&xecfg, 0, sizeof(xecfg));
if (p->status == 0)
return error("internal diff status error");
if (p->status == DIFF_STATUS_UNKNOWN)
@@ -2774,7 +2915,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
return error("unable to read files to diff");
/* Maybe hash p->two? into the patch id? */
if (file_is_binary(p->two))
if (diff_filespec_is_binary(p->two))
continue;
len1 = remove_space(p->one->path, strlen(p->one->path));
@@ -3001,6 +3142,7 @@ void diffcore_std(struct diff_options *options)
{
if (options->quiet)
return;
if (options->break_opt != -1)
diffcore_break(options->break_opt);
if (options->detect_rename)

View File

@@ -66,8 +66,7 @@ static int should_break(struct diff_filespec *src,
if (base_size < MINIMUM_BREAK_SIZE)
return 0; /* we do not break too small filepair */
if (diffcore_count_changes(src->data, src->size,
dst->data, dst->size,
if (diffcore_count_changes(src, dst,
NULL, NULL,
0,
&src_copied, &literal_added))

View File

@@ -5,23 +5,20 @@
/*
* Idea here is very simple.
*
* We have total of (sz-N+1) N-byte overlapping sequences in buf whose
* size is sz. If the same N-byte sequence appears in both source and
* destination, we say the byte that starts that sequence is shared
* between them (i.e. copied from source to destination).
* Almost all data we are interested in are text, but sometimes we have
* to deal with binary data. So we cut them into chunks delimited by
* LF byte, or 64-byte sequence, whichever comes first, and hash them.
*
* For each possible N-byte sequence, if the source buffer has more
* instances of it than the destination buffer, that means the
* difference are the number of bytes not copied from source to
* destination. If the counts are the same, everything was copied
* from source to destination. If the destination has more,
* everything was copied, and destination added more.
* For those chunks, if the source buffer has more instances of it
* than the destination buffer, that means the difference are the
* number of bytes not copied from source to destination. If the
* counts are the same, everything was copied from source to
* destination. If the destination has more, everything was copied,
* and destination added more.
*
* We are doing an approximation so we do not really have to waste
* memory by actually storing the sequence. We just hash them into
* somewhere around 2^16 hashbuckets and count the occurrences.
*
* The length of the sequence is arbitrarily set to 8 for now.
*/
/* Wild guess at the initial hash size */
@@ -125,11 +122,14 @@ static struct spanhash_top *add_spanhash(struct spanhash_top *top,
}
}
static struct spanhash_top *hash_chars(unsigned char *buf, unsigned int sz)
static struct spanhash_top *hash_chars(struct diff_filespec *one)
{
int i, n;
unsigned int accum1, accum2, hashval;
struct spanhash_top *hash;
unsigned char *buf = one->data;
unsigned int sz = one->size;
int is_text = !diff_filespec_is_binary(one);
i = INITIAL_HASH_SIZE;
hash = xmalloc(sizeof(*hash) + sizeof(struct spanhash) * (1<<i));
@@ -143,6 +143,11 @@ static struct spanhash_top *hash_chars(unsigned char *buf, unsigned int sz)
unsigned int c = *buf++;
unsigned int old_1 = accum1;
sz--;
/* Ignore CR in CRLF sequence if text */
if (is_text && c == '\r' && sz && *buf == '\n')
continue;
accum1 = (accum1 << 7) ^ (accum2 >> 25);
accum2 = (accum2 << 7) ^ (old_1 >> 25);
accum1 += c;
@@ -156,8 +161,8 @@ static struct spanhash_top *hash_chars(unsigned char *buf, unsigned int sz)
return hash;
}
int diffcore_count_changes(void *src, unsigned long src_size,
void *dst, unsigned long dst_size,
int diffcore_count_changes(struct diff_filespec *src,
struct diff_filespec *dst,
void **src_count_p,
void **dst_count_p,
unsigned long delta_limit,
@@ -172,14 +177,14 @@ int diffcore_count_changes(void *src, unsigned long src_size,
if (src_count_p)
src_count = *src_count_p;
if (!src_count) {
src_count = hash_chars(src, src_size);
src_count = hash_chars(src);
if (src_count_p)
*src_count_p = src_count;
}
if (dst_count_p)
dst_count = *dst_count_p;
if (!dst_count) {
dst_count = hash_chars(dst, dst_size);
dst_count = hash_chars(dst);
if (dst_count_p)
*dst_count_p = dst_count;
}

View File

@@ -138,6 +138,7 @@ struct diff_score {
int src; /* index in rename_src */
int dst; /* index in rename_dst */
int score;
int name_score;
};
static int estimate_similarity(struct diff_filespec *src,
@@ -189,8 +190,7 @@ static int estimate_similarity(struct diff_filespec *src,
delta_limit = (unsigned long)
(base_size * (MAX_SCORE-minimum_score) / MAX_SCORE);
if (diffcore_count_changes(src->data, src->size,
dst->data, dst->size,
if (diffcore_count_changes(src, dst,
&src->cnt_data, &dst->cnt_data,
delta_limit,
&src_copied, &literal_added))
@@ -201,11 +201,8 @@ static int estimate_similarity(struct diff_filespec *src,
*/
if (!dst->size)
score = 0; /* should not happen */
else {
else
score = (int)(src_copied * MAX_SCORE / max_size);
if (basename_same(src, dst))
score++;
}
return score;
}
@@ -242,6 +239,10 @@ static void record_rename_pair(int dst_index, int src_index, int score)
static int score_compare(const void *a_, const void *b_)
{
const struct diff_score *a = a_, *b = b_;
if (a->score == b->score)
return b->name_score - a->name_score;
return b->score - a->score;
}
@@ -360,6 +361,7 @@ void diffcore_rename(struct diff_options *options)
m->dst = i;
m->score = estimate_similarity(one, two,
minimum_score);
m->name_score = basename_same(one, two);
diff_free_filespec_data(one);
}
/* We do not need the text anymore */

View File

@@ -27,6 +27,7 @@ struct diff_filespec {
char *path;
void *data;
void *cnt_data;
const char *funcname_pattern_ident;
unsigned long size;
int xfrm_flags; /* for use by the xfrm */
unsigned short mode; /* file mode */
@@ -37,6 +38,8 @@ struct diff_filespec {
#define DIFF_FILE_VALID(spec) (((spec)->mode) != 0)
unsigned should_free : 1; /* data should be free()'ed */
unsigned should_munmap : 1; /* data should be munmap()'ed */
unsigned checked_attr : 1;
unsigned is_binary : 1; /* data should be considered "binary" */
};
extern struct diff_filespec *alloc_filespec(const char *);
@@ -45,6 +48,7 @@ extern void fill_filespec(struct diff_filespec *, const unsigned char *,
extern int diff_populate_filespec(struct diff_filespec *, int);
extern void diff_free_filespec_data(struct diff_filespec *);
extern int diff_filespec_is_binary(struct diff_filespec *);
struct diff_filepair {
struct diff_filespec *one;
@@ -103,8 +107,8 @@ void diff_debug_queue(const char *, struct diff_queue_struct *);
#define diff_debug_queue(a,b) do {} while(0)
#endif
extern int diffcore_count_changes(void *src, unsigned long src_size,
void *dst, unsigned long dst_size,
extern int diffcore_count_changes(struct diff_filespec *src,
struct diff_filespec *dst,
void **src_count_p,
void **dst_count_p,
unsigned long delta_limit,

View File

@@ -12,6 +12,7 @@
char git_default_email[MAX_GITNAME];
char git_default_name[MAX_GITNAME];
int trust_executable_bit = 1;
int quote_path_fully = 1;
int has_symlinks = 1;
int assume_unchanged;
int prefer_symlink_refs;
@@ -29,6 +30,7 @@ int core_compression_seen;
size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
size_t delta_base_cache_limit = 16 * 1024 * 1024;
char *pager_program;
int pager_in_use;
int pager_use_color = 1;
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */

View File

@@ -26,9 +26,16 @@ Format of STDIN stream:
lf;
commit_msg ::= data;
file_change ::= file_clr | file_del | file_obm | file_inm;
file_change ::= file_clr
| file_del
| file_rnm
| file_cpy
| file_obm
| file_inm;
file_clr ::= 'deleteall' lf;
file_del ::= 'D' sp path_str lf;
file_rnm ::= 'R' sp path_str sp path_str lf;
file_cpy ::= 'C' sp path_str sp path_str lf;
file_obm ::= 'M' sp mode sp (hexsha1 | idnum) sp path_str lf;
file_inm ::= 'M' sp mode sp 'inline' sp path_str lf
data;
@@ -622,6 +629,31 @@ static void release_tree_entry(struct tree_entry *e)
avail_tree_entry = e;
}
static struct tree_content *dup_tree_content(struct tree_content *s)
{
struct tree_content *d;
struct tree_entry *a, *b;
unsigned int i;
if (!s)
return NULL;
d = new_tree_content(s->entry_count);
for (i = 0; i < s->entry_count; i++) {
a = s->entries[i];
b = new_tree_entry();
memcpy(b, a, sizeof(*a));
if (a->tree && is_null_sha1(b->versions[1].sha1))
b->tree = dup_tree_content(a->tree);
else
b->tree = NULL;
d->entries[i] = b;
}
d->entry_count = s->entry_count;
d->delta_depth = s->delta_depth;
return d;
}
static void start_packfile(void)
{
static char tmpfile[PATH_MAX];
@@ -1154,7 +1186,8 @@ static int tree_content_set(
struct tree_entry *root,
const char *p,
const unsigned char *sha1,
const uint16_t mode)
const uint16_t mode,
struct tree_content *subtree)
{
struct tree_content *t = root->tree;
const char *slash1;
@@ -1168,20 +1201,22 @@ static int tree_content_set(
n = strlen(p);
if (!n)
die("Empty path component found in input");
if (!slash1 && !S_ISDIR(mode) && subtree)
die("Non-directories cannot have subtrees");
for (i = 0; i < t->entry_count; i++) {
e = t->entries[i];
if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
if (!slash1) {
if (e->versions[1].mode == mode
if (!S_ISDIR(mode)
&& e->versions[1].mode == mode
&& !hashcmp(e->versions[1].sha1, sha1))
return 0;
e->versions[1].mode = mode;
hashcpy(e->versions[1].sha1, sha1);
if (e->tree) {
if (e->tree)
release_tree_content_recursive(e->tree);
e->tree = NULL;
}
e->tree = subtree;
hashclr(root->versions[1].sha1);
return 1;
}
@@ -1191,7 +1226,7 @@ static int tree_content_set(
}
if (!e->tree)
load_tree(e);
if (tree_content_set(e, slash1 + 1, sha1, mode)) {
if (tree_content_set(e, slash1 + 1, sha1, mode, subtree)) {
hashclr(root->versions[1].sha1);
return 1;
}
@@ -1209,9 +1244,9 @@ static int tree_content_set(
if (slash1) {
e->tree = new_tree_content(8);
e->versions[1].mode = S_IFDIR;
tree_content_set(e, slash1 + 1, sha1, mode);
tree_content_set(e, slash1 + 1, sha1, mode, subtree);
} else {
e->tree = NULL;
e->tree = subtree;
e->versions[1].mode = mode;
hashcpy(e->versions[1].sha1, sha1);
}
@@ -1219,7 +1254,10 @@ static int tree_content_set(
return 1;
}
static int tree_content_remove(struct tree_entry *root, const char *p)
static int tree_content_remove(
struct tree_entry *root,
const char *p,
struct tree_entry *backup_leaf)
{
struct tree_content *t = root->tree;
const char *slash1;
@@ -1239,13 +1277,14 @@ static int tree_content_remove(struct tree_entry *root, const char *p)
goto del_entry;
if (!e->tree)
load_tree(e);
if (tree_content_remove(e, slash1 + 1)) {
if (tree_content_remove(e, slash1 + 1, backup_leaf)) {
for (n = 0; n < e->tree->entry_count; n++) {
if (e->tree->entries[n]->versions[1].mode) {
hashclr(root->versions[1].sha1);
return 1;
}
}
backup_leaf = NULL;
goto del_entry;
}
return 0;
@@ -1254,16 +1293,54 @@ static int tree_content_remove(struct tree_entry *root, const char *p)
return 0;
del_entry:
if (e->tree) {
if (backup_leaf)
memcpy(backup_leaf, e, sizeof(*backup_leaf));
else if (e->tree)
release_tree_content_recursive(e->tree);
e->tree = NULL;
}
e->tree = NULL;
e->versions[1].mode = 0;
hashclr(e->versions[1].sha1);
hashclr(root->versions[1].sha1);
return 1;
}
static int tree_content_get(
struct tree_entry *root,
const char *p,
struct tree_entry *leaf)
{
struct tree_content *t = root->tree;
const char *slash1;
unsigned int i, n;
struct tree_entry *e;
slash1 = strchr(p, '/');
if (slash1)
n = slash1 - p;
else
n = strlen(p);
for (i = 0; i < t->entry_count; i++) {
e = t->entries[i];
if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
if (!slash1) {
memcpy(leaf, e, sizeof(*leaf));
if (e->tree && is_null_sha1(e->versions[1].sha1))
leaf->tree = dup_tree_content(e->tree);
else
leaf->tree = NULL;
return 1;
}
if (!S_ISDIR(e->versions[1].mode))
return 0;
if (!e->tree)
load_tree(e);
return tree_content_get(e, slash1 + 1, leaf);
}
}
return 0;
}
static int update_branch(struct branch *b)
{
static const char *msg = "fast-import";
@@ -1629,7 +1706,7 @@ static void file_change_m(struct branch *b)
typename(type), command_buf.buf);
}
tree_content_set(&b->branch_tree, p, sha1, S_IFREG | mode);
tree_content_set(&b->branch_tree, p, sha1, S_IFREG | mode, NULL);
free(p_uq);
}
@@ -1645,10 +1722,61 @@ static void file_change_d(struct branch *b)
die("Garbage after path in: %s", command_buf.buf);
p = p_uq;
}
tree_content_remove(&b->branch_tree, p);
tree_content_remove(&b->branch_tree, p, NULL);
free(p_uq);
}
static void file_change_cr(struct branch *b, int rename)
{
const char *s, *d;
char *s_uq, *d_uq;
const char *endp;
struct tree_entry leaf;
s = command_buf.buf + 2;
s_uq = unquote_c_style(s, &endp);
if (s_uq) {
if (*endp != ' ')
die("Missing space after source: %s", command_buf.buf);
}
else {
endp = strchr(s, ' ');
if (!endp)
die("Missing space after source: %s", command_buf.buf);
s_uq = xmalloc(endp - s + 1);
memcpy(s_uq, s, endp - s);
s_uq[endp - s] = 0;
}
s = s_uq;
endp++;
if (!*endp)
die("Missing dest: %s", command_buf.buf);
d = endp;
d_uq = unquote_c_style(d, &endp);
if (d_uq) {
if (*endp)
die("Garbage after dest in: %s", command_buf.buf);
d = d_uq;
}
memset(&leaf, 0, sizeof(leaf));
if (rename)
tree_content_remove(&b->branch_tree, s, &leaf);
else
tree_content_get(&b->branch_tree, s, &leaf);
if (!leaf.versions[1].mode)
die("Path %s not in branch", s);
tree_content_set(&b->branch_tree, d,
leaf.versions[1].sha1,
leaf.versions[1].mode,
leaf.tree);
free(s_uq);
free(d_uq);
}
static void file_change_deleteall(struct branch *b)
{
release_tree_content_recursive(b->branch_tree.tree);
@@ -1816,6 +1944,10 @@ static void cmd_new_commit(void)
file_change_m(b);
else if (!prefixcmp(command_buf.buf, "D "))
file_change_d(b);
else if (!prefixcmp(command_buf.buf, "R "))
file_change_cr(b, 1);
else if (!prefixcmp(command_buf.buf, "C "))
file_change_cr(b, 0);
else if (!strcmp("deleteall", command_buf.buf))
file_change_deleteall(b);
else

16
fixup-builtins Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
while [ "$1" ]
do
old="$1"
new=$(echo "$1" | sed 's/git-/git /')
echo "Converting '$old' to '$new'"
git ls-files '*.sh' | while read file
do
sed "s/\\<$old\\>/$new/g" < $file > $file.new
chmod --reference=$file $file.new
mv $file.new $file
done
shift
done
git update-index --refresh >& /dev/null
exit 0

View File

@@ -60,17 +60,17 @@ fall_back_3way () {
mkdir "$dotest/patch-merge-tmp-dir"
# First see if the patch records the index info that we can use.
git-apply -z --index-info "$dotest/patch" \
git apply -z --index-info "$dotest/patch" \
>"$dotest/patch-merge-index-info" &&
GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
git-update-index -z --index-info <"$dotest/patch-merge-index-info" &&
git update-index -z --index-info <"$dotest/patch-merge-index-info" &&
GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
git-write-tree >"$dotest/patch-merge-base+" ||
git write-tree >"$dotest/patch-merge-base+" ||
cannot_fallback "Repository lacks necessary blobs to fall back on 3-way merge."
echo Using index info to reconstruct a base tree...
if GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
git-apply $binary --cached <"$dotest/patch"
git apply $binary --cached <"$dotest/patch"
then
mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
@@ -80,7 +80,7 @@ It does not apply to blobs recorded in its index."
fi
test -f "$dotest/patch-merge-index" &&
his_tree=$(GIT_INDEX_FILE="$dotest/patch-merge-index" git-write-tree) &&
his_tree=$(GIT_INDEX_FILE="$dotest/patch-merge-index" git write-tree) &&
orig_tree=$(cat "$dotest/patch-merge-base") &&
rm -fr "$dotest"/patch-merge-* || exit 1
@@ -95,10 +95,7 @@ It does not apply to blobs recorded in its index."
eval GITHEAD_$his_tree='"$SUBJECT"'
export GITHEAD_$his_tree
git-merge-recursive $orig_tree -- HEAD $his_tree || {
if test -d "$GIT_DIR/rr-cache"
then
git-rerere
fi
git rerere
echo Failed to merge in the changes.
exit 1
}
@@ -198,7 +195,7 @@ else
# Start afresh.
mkdir -p "$dotest" || exit
git-mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" || {
git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" || {
rm -fr "$dotest"
exit 1
}
@@ -216,7 +213,7 @@ fi
case "$resolved" in
'')
files=$(git-diff-index --cached --name-only HEAD) || exit
files=$(git diff-index --cached --name-only HEAD) || exit
if [ "$files" ]; then
echo "Dirty index: cannot apply patches (dirty: $files)" >&2
exit 1
@@ -252,10 +249,7 @@ last=`cat "$dotest/last"`
this=`cat "$dotest/next"`
if test "$skip" = t
then
if test -d "$GIT_DIR/rr-cache"
then
git-rerere clear
fi
git rerere clear
this=`expr "$this" + 1`
resume=
fi
@@ -287,14 +281,14 @@ do
# by the user, or the user can tell us to do so by --resolved flag.
case "$resume" in
'')
git-mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
git mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
<"$dotest/$msgnum" >"$dotest/info" ||
stop_here $this
test -s $dotest/patch || {
echo "Patch is empty. Was it split wrong?"
stop_here $this
}
git-stripspace < "$dotest/msg" > "$dotest/msg-clean"
git stripspace < "$dotest/msg" > "$dotest/msg-clean"
;;
esac
@@ -347,7 +341,7 @@ do
case "$resolved$interactive" in
tt)
# This is used only for interactive view option.
git-diff-index -p --cached HEAD >"$dotest/patch"
git diff-index -p --cached HEAD >"$dotest/patch"
;;
esac
esac
@@ -399,7 +393,7 @@ do
case "$resolved" in
'')
git-apply $git_apply_opt $binary --index "$dotest/patch"
git apply $git_apply_opt $binary --index "$dotest/patch"
apply_status=$?
;;
t)
@@ -408,11 +402,11 @@ do
# trust what the user has in the index file and the
# working tree.
resolved=
git-diff-index --quiet --cached HEAD && {
git diff-index --quiet --cached HEAD && {
echo "No changes - did you forget to use 'git add'?"
stop_here_user_resolve $this
}
unmerged=$(git-ls-files -u)
unmerged=$(git ls-files -u)
if test -n "$unmerged"
then
echo "You still have unmerged paths in your index"
@@ -420,10 +414,7 @@ do
stop_here_user_resolve $this
fi
apply_status=0
if test -d "$GIT_DIR/rr-cache"
then
git rerere
fi
git rerere
;;
esac
@@ -433,7 +424,7 @@ do
then
# Applying the patch to an earlier tree and merging the
# result may have produced the same tree as ours.
git-diff-index --quiet --cached HEAD && {
git diff-index --quiet --cached HEAD && {
echo No changes -- Patch already applied.
go_next
continue
@@ -453,12 +444,12 @@ do
"$GIT_DIR"/hooks/pre-applypatch || stop_here $this
fi
tree=$(git-write-tree) &&
tree=$(git write-tree) &&
echo Wrote tree $tree &&
parent=$(git-rev-parse --verify HEAD) &&
commit=$(git-commit-tree $tree -p $parent <"$dotest/final-commit") &&
parent=$(git rev-parse --verify HEAD) &&
commit=$(git commit-tree $tree -p $parent <"$dotest/final-commit") &&
echo Committed: $commit &&
git-update-ref -m "$GIT_REFLOG_ACTION: $SUBJECT" HEAD $commit $parent ||
git update-ref -m "$GIT_REFLOG_ACTION: $SUBJECT" HEAD $commit $parent ||
stop_here $this
if test -x "$GIT_DIR"/hooks/post-applypatch

Some files were not shown because too many files have changed in this diff Show More