author | Junio C Hamano <junkio@cox.net> | |
Wed, 25 Apr 2007 05:02:38 +0000 (22:02 -0700) | ||
committer | Junio C Hamano <junkio@cox.net> | |
Wed, 25 Apr 2007 05:02:38 +0000 (22:02 -0700) |
* 'master' of git://repo.or.cz/git/fastimport:
fast-import: size_t vs ssize_t
fix importing of subversion tars
Don't repack existing objects in fast-import
fast-import: size_t vs ssize_t
fix importing of subversion tars
Don't repack existing objects in fast-import
96 files changed:
diff --git a/.gitignore b/.gitignore
index 9229e918cd1c8a43b6c73b0083ddfe973b1e3b88..4dc0c395fa7d6e7cff7588ee66c928c954cc014d 100644 (file)
--- a/.gitignore
+++ b/.gitignore
git-branch
git-bundle
git-cat-file
+git-check-attr
git-check-ref-format
git-checkout
git-checkout-index
test-date
test-delta
test-dump-cache-tree
+test-genrandom
test-match-trees
common-cmds.h
*.tar.gz
index b98d21e98e008c79d2598c6aa9213826d26f5b0d..a37b2152bd26be2c2289e1f57a292534a51a93c7 100644 (file)
--- a/Documentation/.gitignore
+++ b/Documentation/.gitignore
*.xml
*.html
-*.1
-*.7
+*.[1-8]
*.made
howto-index.txt
doc.dep
diff --git a/Documentation/Makefile b/Documentation/Makefile
index a637d8d559b6a41505e5381b92d523e55f4b3be8..3f92783d55b401a5c919f7c9176a12953d0c4904 100644 (file)
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
$(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \
$(wildcard git-*.txt)) \
gitk.txt
+MAN5_TXT=gitattributes.txt
MAN7_TXT=git.txt
-DOC_HTML=$(patsubst %.txt,%.html,$(MAN1_TXT) $(MAN7_TXT))
+DOC_HTML=$(patsubst %.txt,%.html,$(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT))
ARTICLES = tutorial
ARTICLES += tutorial-2
DOC_HTML += $(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES))
DOC_MAN1=$(patsubst %.txt,%.1,$(MAN1_TXT))
+DOC_MAN5=$(patsubst %.txt,%.5,$(MAN5_TXT))
DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
prefix?=$(HOME)
bindir?=$(prefix)/bin
mandir?=$(prefix)/man
man1dir=$(mandir)/man1
+man5dir=$(mandir)/man5
man7dir=$(mandir)/man7
# DESTDIR=
html: $(DOC_HTML)
-$(DOC_HTML) $(DOC_MAN1) $(DOC_MAN7): asciidoc.conf
+$(DOC_HTML) $(DOC_MAN1) $(DOC_MAN5) $(DOC_MAN7): asciidoc.conf
-man: man1 man7
+man: man1 man5 man7
man1: $(DOC_MAN1)
+man5: $(DOC_MAN5)
man7: $(DOC_MAN7)
install: man
- $(INSTALL) -d -m755 $(DESTDIR)$(man1dir) $(DESTDIR)$(man7dir)
+ $(INSTALL) -d -m755 $(DESTDIR)$(man1dir)
+ $(INSTALL) -d -m755 $(DESTDIR)$(man5dir)
+ $(INSTALL) -d -m755 $(DESTDIR)$(man7dir)
$(INSTALL) -m644 $(DOC_MAN1) $(DESTDIR)$(man1dir)
+ $(INSTALL) -m644 $(DOC_MAN5) $(DESTDIR)$(man5dir)
$(INSTALL) -m644 $(DOC_MAN7) $(DESTDIR)$(man7dir)
git.7 git.html: git.txt core-intro.txt
clean:
- rm -f *.xml *.xml+ *.html *.html+ *.1 *.7 howto-index.txt howto/*.html doc.dep
+ rm -f *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 howto-index.txt howto/*.html doc.dep
rm -f $(cmds_txt) *.made
%.html : %.txt
sed -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' >$@+
mv $@+ $@
-%.1 %.7 : %.xml
+%.1 %.5 %.7 : %.xml
xmlto -m callouts.xsl man $<
%.xml : %.txt
index f58268f6be37d0de6dbc4e34de1dd539a7504e84..d88456306c503d9e604ffbb699fb7cadacb2e733 100644 (file)
-GIT v1.5.1.2 Release Notes (draft)
+GIT v1.5.1.2 Release Notes
==========================
Fixes since v1.5.1.1
* Bugfixes
+ - "git clone" over http from a repository that has lost the
+ loose refs by running "git pack-refs" were broken (a code to
+ deal with this was added to "git fetch" in v1.5.0, but it
+ was missing from "git clone").
+
- "git diff a/ b/" incorrectly fell in "diff between two
filesystem objects" codepath, when the user most likely
wanted to limit the extent of output to two tracked
- git-blame on a very long working tree path had buffer
overrun problem.
+ - git-apply did not like to be fed two patches in a row that created
+ and then modified the same file.
+
+ - git-svn was confused when a non-project was stored directly under
+ trunk/, branches/ and tags/.
+
+ - git-svn wants the Error.pm module that was at least as new
+ as what we ship as part of git; install ours in our private
+ installation location if the one on the system is older.
+
+ - An earlier update to command line integer parameter parser was
+ botched and made 'update-index --cacheinfo' completely useless.
+
+
* Documentation updates
- Various documentation updates from J. Bruce Fields, Frank
Lichtenheld, Alex Riesen and others. Andrew Ruder started a
war on undocumented options.
-
----
-exec >/var/tmp/1
-O=v1.5.1.1-31-g0220f1e
-echo O=`git describe refs/heads/maint`
-git shortlog --no-merges $O..refs/heads/maint
index d93da608c70c3544963ad7dfd50ca12d39bc7fa9..abecac6de9765f83a1fac574c8c722488f442f9e 100644 (file)
Updates since v1.5.1
--------------------
+* Plumbing level subproject support.
+
+ You can include a subdirectory that has an independent git
+ repository in your index and tree objects as a
+ "subproject". This plumbing (i.e. "core") level subproject
+ support explicitly excludes recursive behaviour.
+
+ The "subproject" entries in the index and trees are
+ incompatible with older versions of git. Experimenting with
+ the plumbing level support is encouraged, but be warned that
+ unless everybody in your project updates to this release or
+ later, using this feature would make your project
+ inaccessible by people with older versions of git.
+
+* Plumbing level gitattributes support.
+
+ The gitattributes mechanism allows you to add 'attributes' to
+ paths in your project, and affect the way certain git
+ operations work. Currently you can influence if a path is
+ considered a binary or text (the former would be treated by
+ 'git diff' not to produce textual output; the latter can go
+ through the line endings conversion process in repositories
+ with core.autocrlf set), and specify a custom 3-way merge
+ driver.
+
+* The packfile format now optionally suports 64-bit index.
+
+ This release supports the "version 2" format of the .idx
+ file. This is automatically enabled when a huge packfile
+ needs more than 32-bit to express offsets of objects in the
+ pack
+
* New commands and options.
- "git bisect start" can optionally take a single bad commit and
- "git format-patch" learned a new --subject-prefix=<string>
option, to override the built-in "[PATCH]".
+ - "git add -u" is a quick way to do the first stage of "git
+ commit -a" (i.e. update the index to match the working
+ tree); it obviously does not make a commit.
+
* Updated behavior of existing commands.
- "git diff --stat" shows size of preimage and postimage blobs
- "git archive" does not insist you to give --format parameter
anymore; it defaults to "tar".
+ - "git cvsserver" can use backends other than sqlite.
+
+ - "gitview" (in contrib/ section) learned to better support
+ "git-annotate".
+
+ - Local "git fetch" from a repository whose object store is
+ one of the alternates (e.g. fetching from the origin in a
+ repository created with "git clone -l -s") avoids
+ downloading objects unnecessary.
+
* Builds
- git-p4import has never been installed; now there is an
Fixes since v1.5.1
------------------
-The following are all in v1.5.1.x series, unless otherwise noted.
-
-* Documentation updates
-
- - Various documentation updates from J. Bruce Fields, Frank
- Lichtenheld, Alex Riesen and others. Andrew Ruder started a
- war on undocumented options.
+All of the fixes in v1.5.1 maintenance series are included in
+this release, unless otherwise noted.
* Bugfixes
- - "git diff a/ b/" incorrectly fell in "diff between two
- filesystem objects" codepath, when the user most likely
- wanted to limit the extent of output to two tracked
- directories.
-
- - git-quiltimport had the same bug as we fixed for
- git-applymbox in v1.5.1.1 -- it gave an alarming "did not
- have any patch" message (but did not actually fail and was
- harmless).
-
- - various git-svn fixes.
-
- - Sample update hook incorrectly always refused requests to
- delete branches through push.
-
- - git-blame on a very long working tree path had buffer
- overrun problem.
-
- Switching branches with "git checkout" refused to work when
a path changes from a file to a directory between the
current branch and the new branch, in order not to lose
will not be backported to 1.5.1.x series, as it is rather an
intrusive change.
+* Documentation updates
+
* Performance Tweaks
--
exec >/var/tmp/1
-O=v1.5.1.1-158-g86da9de
+O=v1.5.1.2-242-g2d76548
echo O=`git describe refs/heads/master`
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
index 0381590d383c4fffbf04895be4c47e407dd94e80..443802a9a3e4982c33f831217602ea600f0f2775 100755 (executable)
git-cat-file plumbinginterrogators
git-checkout-index plumbingmanipulators
git-checkout mainporcelain
+git-check-attr purehelpers
git-check-ref-format purehelpers
git-cherry ancillaryinterrogators
git-cherry-pick mainporcelain
index 2c0a66632329dae8ae1bf9412a57ab247cc8a603..e0aff5369f3b7ee3059095fc3531779e452e1db1 100644 (file)
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
branch.<name>.merge to the desired branch, and use the special setting
`.` (a period) for branch.<name>.remote.
+clean.requireForce::
+ A boolean to make git-clean do nothing unless given -f or -n. Defaults
+ to false.
+
color.branch::
A boolean to enable/disable color in the output of
gitlink:git-branch[1]. May be set to `true` (or `always`),
conflicts, 2 outputs conflicts and file changes. Level 5 and
above outputs debugging information. The default is level 2.
+merge.<driver>.name::
+ Defines a human readable name for a custom low-level
+ merge driver. See gitlink:gitattributes[5] for details.
+
+merge.<driver>.driver::
+ Defines the command that implements a custom low-level
+ merge driver. See gitlink:gitattributes[5] for details.
+
+merge.<driver>.recursive::
+ Names a low-level merge driver to be used when
+ performing an internal merge between common ancestors.
+ See gitlink:gitattributes[5] for details.
+
pack.window::
The size of the window used by gitlink:git-pack-objects[1] when no
window size is given on the command line. Defaults to 10.
diff --git a/Documentation/git-check-attr.txt b/Documentation/git-check-attr.txt
--- /dev/null
@@ -0,0 +1,37 @@
+git-check-attr(1)
+=================
+
+NAME
+----
+git-check-attr - Display gitattributes information.
+
+
+SYNOPSIS
+--------
+'git-check-attr' attr... [--] pathname...
+
+DESCRIPTION
+-----------
+For every pathname, this command will list if each attr is 'unspecified',
+'set', or 'unset' as a gitattribute on that pathname.
+
+OPTIONS
+-------
+\--::
+ Interpret all preceding arguments as attributes, and all following
+ arguments as path names. If not supplied, only the first argument will
+ be treated as an attribute.
+
+
+Author
+------
+Written by Junio C Hamano <junkio@cox.net>
+
+Documentation
+--------------
+Documentation by James Bowes.
+
+GIT
+---
+Part of the gitlink:git[7] suite
+
index 4f2e847dc3fe44f503a578fc79496179c21ac11a..918d8ee720a573f91ec6efa654da5c2cdc05d7b0 100644 (file)
SYNOPSIS
--------
[verse]
-'git-checkout' [-q] [-f] [-b [--track | --no-track] <new_branch> [-l]] [-m] [<branch>]
+'git-checkout' [-q] [-f] [[--track | --no-track] -b <new_branch> [-l]] [-m] [<branch>]
'git-checkout' [<tree-ish>] <paths>...
DESCRIPTION
index c61afbcdbac49ccbb7ee755e3fdb19d7d77225d0..5aff026eb489a84640ffef6c8deed5b9f89e2b06 100644 (file)
SYNOPSIS
--------
[verse]
-'git-clean' [-d] [-n] [-q] [-x | -X] [--] <paths>...
+'git-clean' [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...
DESCRIPTION
-----------
-d::
Remove untracked directories in addition to untracked files.
+-f::
+ If the git configuration specifies clean.forceRequire as true,
+ git-clean will refuse to run unless given -f or -n.
+
-n::
Don't actually remove anything, just show what would be done.
index 2229ee86b72865c656112aae87b9593d199bc2b8..b7a49b9f58af24844e0eae486d1cfec72bc518c6 100644 (file)
message can later be searched for within all .keep files to
locate any which have outlived their usefulness.
+--index-version=<version>[,<offset>]::
+ This is intended to be used by the test suite only. It allows
+ to force the version for the generated pack index, and to force
+ 64-bit index entries on objects located above the given offset.
+
Note
----
index fdc6f9728921e3bf71a57d441c99c8fb7d280803..d9e11c65344a52fd81023e26cc1535ac825106dd 100644 (file)
length, this option typically shrinks the resulting
packfile by 3-5 per-cent.
+--index-version=<version>[,<offset>]::
+ This is intended to be used by the test suite only. It allows
+ to force the version for the generated pack index, and to force
+ 64-bit index entries on objects located above the given offset.
+
Author
------
index 5b55cda512bbdb507486ac5d0db0d28e32b1dcb1..19c5b9bbda60744723e453dceaa209c47a49cb60 100644 (file)
<3> "reset" copies the old head to .git/ORIG_HEAD; redo the
commit by starting with its log message. If you do not need to
edit the message further, you can give -C option instead.
++
+See also the --amend option to gitlink:git-commit[1].
Undo commits permanently::
+
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 9defc332736e1b6297ac003e491706d5ce32e834..ca1f78f790149d845e7f74fc8456399589e2e5b9 100644 (file)
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
* link:RelNotes-1.5.1.txt[release notes for 1.5.1]
-* link:v1.5.0.7/git.html[documentation for release 1.5.0.7]
+* link:v1.5.1.2/git.html[documentation for release 1.5.1.2]
+
+* link:RelNotes-1.5.1.2.txt[release notes for 1.5.1.2]
+
+* link:RelNotes-1.5.1.1.txt[release notes for 1.5.1.1]
* link:RelNotes-1.5.0.7.txt[release notes for 1.5.0.7]
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
--- /dev/null
@@ -0,0 +1,313 @@
+gitattributes(5)
+================
+
+NAME
+----
+gitattributes - defining attributes per path
+
+SYNOPSIS
+--------
+$GIT_DIR/info/attributes, gitattributes
+
+
+DESCRIPTION
+-----------
+
+A `gitattributes` file is a simple text file that gives
+`attributes` to pathnames.
+
+Each line in `gitattributes` file is of form:
+
+ glob attr1 attr2 ...
+
+That is, a glob pattern followed by an attributes list,
+separated by whitespaces. When the glob pattern matches the
+path in question, the attributes listed on the line are given to
+the path.
+
+Each attribute can be in one of these states for a given path:
+
+Set::
+
+ The path has the attribute with special value "true";
+ this is specified by listing only the name of the
+ attribute in the attribute list.
+
+Unset::
+
+ The path has the attribute with special value "false";
+ this is specified by listing the name of the attribute
+ prefixed with a dash `-` in the attribute list.
+
+Set to a value::
+
+ The path has the attribute with specified string value;
+ this is specified by listing the name of the attribute
+ followed by an equal sign `=` and its value in the
+ attribute list.
+
+Unspecified::
+
+ No glob pattern matches the path, and nothing says if
+ the path has or does not have the attribute, the
+ attribute for the path is said to be Unspecified.
+
+When more than one glob pattern matches the path, a later line
+overrides an earlier line. This overriding is done per
+attribute.
+
+When deciding what attributes are assigned to a path, git
+consults `$GIT_DIR/info/attributes` file (which has the highest
+precedence), `.gitattributes` file in the same directory as the
+path in question, and its parent directories (the further the
+directory that contains `.gitattributes` is from the path in
+question, the lower its precedence).
+
+Sometimes you would need to override an setting of an attribute
+for a path to `unspecified` state. This can be done by listing
+the name of the attribute prefixed with an exclamation point `!`.
+
+
+EFFECTS
+-------
+
+Certain operations by git can be influenced by assigning
+particular attributes to a path. Currently, three operations
+are attributes-aware.
+
+Checking-out and checking-in
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The attribute `crlf` affects how the contents stored in the
+repository are copied to the working tree files when commands
+such as `git checkout` and `git merge` run. It also affects how
+git stores the contents you prepare in the working tree in the
+repository upon `git add` and `git commit`.
+
+Set::
+
+ Setting the `crlf` attribute on a path is meant to mark
+ the path as a "text" file. 'core.autocrlf' conversion
+ takes place without guessing the content type by
+ inspection.
+
+Unset::
+
+ Unsetting the `crlf` attribute on a path is meant to
+ mark the path as a "binary" file. The path never goes
+ through line endings conversion upon checkin/checkout.
+
+Unspecified::
+
+ Unspecified `crlf` attribute tells git to apply the
+ `core.autocrlf` conversion when the file content looks
+ like text.
+
+Set to string value "input"::
+
+ This is similar to setting the attribute to `true`, but
+ also forces git to act as if `core.autocrlf` is set to
+ `input` for the path.
+
+Any other value set to `crlf` attribute is ignored and git acts
+as if the attribute is left unspecified.
+
+
+The `core.autocrlf` conversion
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If the configuration variable `core.autocrlf` is false, no
+conversion is done.
+
+When `core.autocrlf` is true, it means that the platform wants
+CRLF line endings for files in the working tree, and you want to
+convert them back to the normal LF line endings when checking
+in to the repository.
+
+When `core.autocrlf` is set to "input", line endings are
+converted to LF upon checkin, but there is no conversion done
+upon checkout.
+
+
+Generating diff text
+~~~~~~~~~~~~~~~~~~~~
+
+The attribute `diff` affects if `git diff` generates textual
+patch for the path or just says `Binary files differ`.
+
+Set::
+
+ A path to which the `diff` attribute is set is treated
+ as text, even when they contain byte values that
+ normally never appear in text files, such as NUL.
+
+Unset::
+
+ A path to which the `diff` attribute is unset will
+ generate `Binary files differ`.
+
+Unspecified::
+
+ A path to which the `diff` attribute is unspecified
+ first gets its contents inspected, and if it looks like
+ text, it is treated as text. Otherwise it would
+ generate `Binary files differ`.
+
+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.
+
+
+Defining a custom diff driver
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The definition of a diff driver is done in `gitconfig`, not
+`gitattributes` file, so strictly speaking this manual page is a
+wrong place to talk about it. However...
+
+To define a custom diff driver `jcdiff`, add a section to your
+`$GIT_DIR/config` file (or `$HOME/.gitconfig` file) like this:
+
+----------------------------------------------------------------
+[diff "jcdiff"]
+ command = j-c-diff
+----------------------------------------------------------------
+
+When git needs to show you a diff for the path with `diff`
+attribute set to `jcdiff`, it calls the command you specified
+with the above configuration, i.e. `j-c-diff`, with 7
+parameters, just like `GIT_EXTERNAL_DIFF` program is called.
+See gitlink:git[7] for details.
+
+
+Performing a three-way merge
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The attribute `merge` affects how three versions of a file is
+merged when a file-level merge is necessary during `git merge`,
+and other programs such as `git revert` and `git cherry-pick`.
+
+Set::
+
+ Built-in 3-way merge driver is used to merge the
+ contents in a way similar to `merge` command of `RCS`
+ suite. This is suitable for ordinary text files.
+
+Unset::
+
+ Take the version from the current branch as the
+ tentative merge result, and declare that the merge has
+ conflicts. This is suitable for binary files that does
+ not have a well-defined merge semantics.
+
+Unspecified::
+
+ By default, this uses the same built-in 3-way merge
+ driver as is the case the `merge` attribute is set.
+ However, `merge.default` configuration variable can name
+ different merge driver to be used for paths to which the
+ `merge` attribute is unspecified.
+
+String::
+
+ 3-way merge is performed using the specified custom
+ merge driver. The built-in 3-way merge driver can be
+ explicitly specified by asking for "text" driver; the
+ built-in "take the current branch" driver can be
+ requested with "binary".
+
+
+Defining a custom merge driver
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The definition of a merge driver is done in `gitconfig` not
+`gitattributes` file, so strictly speaking this manual page is a
+wrong place to talk about it. However...
+
+To define a custom merge driver `filfre`, add a section to your
+`$GIT_DIR/config` file (or `$HOME/.gitconfig` file) like this:
+
+----------------------------------------------------------------
+[merge "filfre"]
+ name = feel-free merge driver
+ driver = filfre %O %A %B
+ recursive = binary
+----------------------------------------------------------------
+
+The `merge.*.name` variable gives the driver a human-readable
+name.
+
+The `merge.*.driver` variable's value is used to construct a
+command to run to merge ancestor's version (`%O`), current
+version (`%A`) and the other branches' version (`%B`). These
+three tokens are replaced with the names of temporary files that
+hold the contents of these versions when the command line is
+built.
+
+The merge driver is expected to leave the result of the merge in
+the file named with `%A` by overwriting it, and exit with zero
+status if it managed to merge them cleanly, or non-zero if there
+were conflicts.
+
+The `merge.*.recursive` variable specifies what other merge
+driver to use when the merge driver is called for an internal
+merge between common ancestors, when there are more than one.
+When left unspecified, the driver itself is used for both
+internal merge and the final merge.
+
+
+EXAMPLE
+-------
+
+If you have these three `gitattributes` file:
+
+----------------------------------------------------------------
+(in $GIT_DIR/info/attributes)
+
+a* foo !bar -baz
+
+(in .gitattributes)
+abc foo bar baz
+
+(in t/.gitattributes)
+ab* merge=filfre
+abc -foo -bar
+*.c frotz
+----------------------------------------------------------------
+
+the attributes given to path `t/abc` are computed as follows:
+
+1. By examining `t/.gitattributes` (which is in the same
+ diretory as the path in question), git finds that the first
+ line matches. `merge` attribute is set. It also finds that
+ the second line matches, and attributes `foo` and `bar`
+ are unset.
+
+2. Then it examines `.gitattributes` (which is in the parent
+ directory), and finds that the first line matches, but
+ `t/.gitattributes` file already decided how `merge`, `foo`
+ and `bar` attributes should be given to this path, so it
+ leaves `foo` and `bar` unset. Attribute `baz` is set.
+
+3. Finally it examines `$GIT_DIR/info/gitattributes`. This file
+ is used to override the in-tree settings. The first line is
+ a match, and `foo` is set, `bar` is reverted to unspecified
+ state, and `baz` is unset.
+
+As the result, the attributes assignement to `t/abc` becomes:
+
+----------------------------------------------------------------
+foo set to true
+bar unspecified
+baz set to false
+merge set to string value "filfre"
+frotz unspecified
+----------------------------------------------------------------
+
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 2325660ff42bf7f6e1c998723fd72f150c347c7d..41ee8b4ea2af9e25f7ac2ad56c083108080de49e 100755 (executable)
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.5.1.1.GIT
+DEF_VER=v1.5.1.2.GIT
LF='
'
diff --git a/Makefile b/Makefile
index 251fc31fc05035bf85265d3ea56c8b3ab24c895f..65bd2dbf9d8089ba8c7bf20479605ef3b02b6a49 100644 (file)
--- a/Makefile
+++ b/Makefile
diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
- utf8.h reflog-walk.h patch-ids.h decorate.h
+ utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h
DIFF_OBJS = \
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
write_or_die.o trace.o list-objects.o grep.o match-trees.o \
alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
- convert.o decorate.o
+ convert.o attr.o decorate.o progress.o
BUILTIN_OBJS = \
builtin-add.o \
builtin-branch.o \
builtin-bundle.o \
builtin-cat-file.o \
+ builtin-check-attr.o \
builtin-checkout-index.o \
builtin-check-ref-format.o \
builtin-commit-tree.o \
export NO_SVN_TESTS
-test: all test-chmtime$X
+test: all test-chmtime$X test-genrandom$X
$(MAKE) -C t/ all
test-date$X: test-date.c date.o ctype.o
test-chmtime$X: test-chmtime.c
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $<
+test-genrandom$X: test-genrandom.c
+ $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $<
+
check-sha1:: test-sha1$X
./test-sha1.sh
gzip -n -9 -f $(htmldocs).tar
:
rm -fr .doc-tmp-dir
- mkdir .doc-tmp-dir .doc-tmp-dir/man1 .doc-tmp-dir/man7
+ mkdir -p .doc-tmp-dir/man1 .doc-tmp-dir/man5 .doc-tmp-dir/man7
$(MAKE) -C Documentation DESTDIR=./ \
man1dir=../.doc-tmp-dir/man1 \
+ man5dir=../.doc-tmp-dir/man5 \
man7dir=../.doc-tmp-dir/man7 \
install
cd .doc-tmp-dir && $(TAR) cf ../$(manpages).tar .
clean:
rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
- test-chmtime$X $(LIB_FILE) $(XDIFF_LIB)
+ test-chmtime$X test-genrandom$X $(LIB_FILE) $(XDIFF_LIB)
rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X
rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
rm -rf autom4te.cache
index 460db192d5a7de4073eb89972871720dd1c8fe1e..216c23a6f854c614d38c743cd7687a37f304161b 100644 (file)
--- a/alloc.c
+++ b/alloc.c
#define BLOCKING 1024
-#define DEFINE_ALLOCATOR(name) \
+#define DEFINE_ALLOCATOR(name, type) \
static unsigned int name##_allocs; \
-struct name *alloc_##name##_node(void) \
+void *alloc_##name##_node(void) \
{ \
static int nr; \
- static struct name *block; \
+ static type *block; \
+ void *ret; \
\
if (!nr) { \
nr = BLOCKING; \
- block = xcalloc(BLOCKING, sizeof(struct name)); \
+ block = xmalloc(BLOCKING * sizeof(type)); \
} \
nr--; \
name##_allocs++; \
- return block++; \
+ ret = block++; \
+ memset(ret, 0, sizeof(type)); \
+ return ret; \
}
-DEFINE_ALLOCATOR(blob)
-DEFINE_ALLOCATOR(tree)
-DEFINE_ALLOCATOR(commit)
-DEFINE_ALLOCATOR(tag)
+union any_object {
+ struct object object;
+ struct blob blob;
+ struct tree tree;
+ struct commit commit;
+ struct tag tag;
+};
+
+DEFINE_ALLOCATOR(blob, struct blob)
+DEFINE_ALLOCATOR(tree, struct tree)
+DEFINE_ALLOCATOR(commit, struct commit)
+DEFINE_ALLOCATOR(tag, struct tag)
+DEFINE_ALLOCATOR(object, union any_object)
#ifdef NO_C99_FORMAT
#define SZ_FMT "%u"
diff --git a/attr.c b/attr.c
--- /dev/null
+++ b/attr.c
@@ -0,0 +1,565 @@
+#include "cache.h"
+#include "attr.h"
+
+const char git_attr__true[] = "(builtin)true";
+const char git_attr__false[] = "\0(builtin)false";
+static const char git_attr__unknown[] = "(builtin)unknown";
+#define ATTR__TRUE git_attr__true
+#define ATTR__FALSE git_attr__false
+#define ATTR__UNSET NULL
+#define ATTR__UNKNOWN git_attr__unknown
+
+/*
+ * The basic design decision here is that we are not going to have
+ * insanely large number of attributes.
+ *
+ * This is a randomly chosen prime.
+ */
+#define HASHSIZE 257
+
+#ifndef DEBUG_ATTR
+#define DEBUG_ATTR 0
+#endif
+
+struct git_attr {
+ struct git_attr *next;
+ unsigned h;
+ int attr_nr;
+ char name[FLEX_ARRAY];
+};
+static int attr_nr;
+
+static struct git_attr_check *check_all_attr;
+static struct git_attr *(git_attr_hash[HASHSIZE]);
+
+static unsigned hash_name(const char *name, int namelen)
+{
+ unsigned val = 0;
+ unsigned char c;
+
+ while (namelen--) {
+ c = *name++;
+ val = ((val << 7) | (val >> 22)) ^ c;
+ }
+ return val;
+}
+
+static int invalid_attr_name(const char *name, int namelen)
+{
+ /*
+ * Attribute name cannot begin with '-' and from
+ * [-A-Za-z0-9_.]. We'd specifically exclude '=' for now,
+ * as we might later want to allow non-binary value for
+ * attributes, e.g. "*.svg merge=special-merge-program-for-svg"
+ */
+ if (*name == '-')
+ return -1;
+ while (namelen--) {
+ char ch = *name++;
+ if (! (ch == '-' || ch == '.' || ch == '_' ||
+ ('0' <= ch && ch <= '9') ||
+ ('a' <= ch && ch <= 'z') ||
+ ('A' <= ch && ch <= 'Z')) )
+ return -1;
+ }
+ return 0;
+}
+
+struct git_attr *git_attr(const char *name, int len)
+{
+ unsigned hval = hash_name(name, len);
+ unsigned pos = hval % HASHSIZE;
+ struct git_attr *a;
+
+ for (a = git_attr_hash[pos]; a; a = a->next) {
+ if (a->h == hval &&
+ !memcmp(a->name, name, len) && !a->name[len])
+ return a;
+ }
+
+ if (invalid_attr_name(name, len))
+ return NULL;
+
+ a = xmalloc(sizeof(*a) + len + 1);
+ memcpy(a->name, name, len);
+ a->name[len] = 0;
+ a->h = hval;
+ a->next = git_attr_hash[pos];
+ a->attr_nr = attr_nr++;
+ git_attr_hash[pos] = a;
+
+ check_all_attr = xrealloc(check_all_attr,
+ sizeof(*check_all_attr) * attr_nr);
+ check_all_attr[a->attr_nr].attr = a;
+ check_all_attr[a->attr_nr].value = ATTR__UNKNOWN;
+ return a;
+}
+
+/*
+ * .gitattributes file is one line per record, each of which is
+ *
+ * (1) glob pattern.
+ * (2) whitespace
+ * (3) whitespace separated list of attribute names, each of which
+ * could be prefixed with '-' to mean "set to false", '!' to mean
+ * "unset".
+ */
+
+/* What does a matched pattern decide? */
+struct attr_state {
+ struct git_attr *attr;
+ const char *setto;
+};
+
+struct match_attr {
+ union {
+ char *pattern;
+ struct git_attr *attr;
+ } u;
+ char is_macro;
+ unsigned num_attr;
+ struct attr_state state[FLEX_ARRAY];
+};
+
+static const char blank[] = " \t\r\n";
+
+static const char *parse_attr(const char *src, int lineno, const char *cp,
+ int *num_attr, struct match_attr *res)
+{
+ const char *ep, *equals;
+ int len;
+
+ ep = cp + strcspn(cp, blank);
+ equals = strchr(cp, '=');
+ if (equals && ep < equals)
+ equals = NULL;
+ if (equals)
+ len = equals - cp;
+ else
+ len = ep - cp;
+ if (!res) {
+ if (*cp == '-' || *cp == '!') {
+ cp++;
+ len--;
+ }
+ if (invalid_attr_name(cp, len)) {
+ fprintf(stderr,
+ "%.*s is not a valid attribute name: %s:%d\n",
+ len, cp, src, lineno);
+ return NULL;
+ }
+ } else {
+ struct attr_state *e;
+
+ e = &(res->state[*num_attr]);
+ if (*cp == '-' || *cp == '!') {
+ e->setto = (*cp == '-') ? ATTR__FALSE : ATTR__UNSET;
+ cp++;
+ len--;
+ }
+ else if (!equals)
+ e->setto = ATTR__TRUE;
+ else {
+ char *value;
+ int vallen = ep - equals;
+ value = xmalloc(vallen);
+ memcpy(value, equals+1, vallen-1);
+ value[vallen-1] = 0;
+ e->setto = value;
+ }
+ e->attr = git_attr(cp, len);
+ }
+ (*num_attr)++;
+ return ep + strspn(ep, blank);
+}
+
+static struct match_attr *parse_attr_line(const char *line, const char *src,
+ int lineno, int macro_ok)
+{
+ int namelen;
+ int num_attr;
+ const char *cp, *name;
+ struct match_attr *res = NULL;
+ int pass;
+ int is_macro;
+
+ cp = line + strspn(line, blank);
+ if (!*cp || *cp == '#')
+ return NULL;
+ name = cp;
+ namelen = strcspn(name, blank);
+ if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen &&
+ !prefixcmp(name, ATTRIBUTE_MACRO_PREFIX)) {
+ if (!macro_ok) {
+ fprintf(stderr, "%s not allowed: %s:%d\n",
+ name, src, lineno);
+ return NULL;
+ }
+ is_macro = 1;
+ name += strlen(ATTRIBUTE_MACRO_PREFIX);
+ name += strspn(name, blank);
+ namelen = strcspn(name, blank);
+ if (invalid_attr_name(name, namelen)) {
+ fprintf(stderr,
+ "%.*s is not a valid attribute name: %s:%d\n",
+ namelen, name, src, lineno);
+ return NULL;
+ }
+ }
+ else
+ is_macro = 0;
+
+ for (pass = 0; pass < 2; pass++) {
+ /* pass 0 counts and allocates, pass 1 fills */
+ num_attr = 0;
+ cp = name + namelen;
+ cp = cp + strspn(cp, blank);
+ while (*cp)
+ cp = parse_attr(src, lineno, cp, &num_attr, res);
+ if (pass)
+ break;
+ res = xcalloc(1,
+ sizeof(*res) +
+ sizeof(struct attr_state) * num_attr +
+ (is_macro ? 0 : namelen + 1));
+ if (is_macro)
+ res->u.attr = git_attr(name, namelen);
+ else {
+ res->u.pattern = (char*)&(res->state[num_attr]);
+ memcpy(res->u.pattern, name, namelen);
+ res->u.pattern[namelen] = 0;
+ }
+ res->is_macro = is_macro;
+ res->num_attr = num_attr;
+ }
+ return res;
+}
+
+/*
+ * Like info/exclude and .gitignore, the attribute information can
+ * come from many places.
+ *
+ * (1) .gitattribute file of the same directory;
+ * (2) .gitattribute file of the parent directory if (1) does not have
+ * any match; this goes recursively upwards, just like .gitignore.
+ * (3) $GIT_DIR/info/attributes, which overrides both of the above.
+ *
+ * In the same file, later entries override the earlier match, so in the
+ * global list, we would have entries from info/attributes the earliest
+ * (reading the file from top to bottom), .gitattribute of the root
+ * directory (again, reading the file from top to bottom) down to the
+ * current directory, and then scan the list backwards to find the first match.
+ * This is exactly the same as what excluded() does in dir.c to deal with
+ * .gitignore
+ */
+
+static struct attr_stack {
+ struct attr_stack *prev;
+ char *origin;
+ unsigned num_matches;
+ struct match_attr **attrs;
+} *attr_stack;
+
+static void free_attr_elem(struct attr_stack *e)
+{
+ int i;
+ free(e->origin);
+ for (i = 0; i < e->num_matches; i++) {
+ struct match_attr *a = e->attrs[i];
+ int j;
+ for (j = 0; j < a->num_attr; j++) {
+ const char *setto = a->state[j].setto;
+ if (setto == ATTR__TRUE ||
+ setto == ATTR__FALSE ||
+ setto == ATTR__UNSET ||
+ setto == ATTR__UNKNOWN)
+ ;
+ else
+ free((char*) setto);
+ }
+ free(a);
+ }
+ free(e);
+}
+
+static const char *builtin_attr[] = {
+ "[attr]binary -diff -crlf",
+ NULL,
+};
+
+static struct attr_stack *read_attr_from_array(const char **list)
+{
+ struct attr_stack *res;
+ const char *line;
+ int lineno = 0;
+
+ res = xcalloc(1, sizeof(*res));
+ while ((line = *(list++)) != NULL) {
+ struct match_attr *a;
+
+ a = parse_attr_line(line, "[builtin]", ++lineno, 1);
+ if (!a)
+ continue;
+ res->attrs = xrealloc(res->attrs,
+ sizeof(struct match_attr *) * (res->num_matches + 1));
+ res->attrs[res->num_matches++] = a;
+ }
+ return res;
+}
+
+static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
+{
+ FILE *fp;
+ struct attr_stack *res;
+ char buf[2048];
+ int lineno = 0;
+
+ res = xcalloc(1, sizeof(*res));
+ fp = fopen(path, "r");
+ if (!fp)
+ return res;
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ struct match_attr *a;
+
+ a = parse_attr_line(buf, path, ++lineno, macro_ok);
+ if (!a)
+ continue;
+ res->attrs = xrealloc(res->attrs,
+ sizeof(struct match_attr *) * (res->num_matches + 1));
+ res->attrs[res->num_matches++] = a;
+ }
+ fclose(fp);
+ return res;
+}
+
+#if DEBUG_ATTR
+static void debug_info(const char *what, struct attr_stack *elem)
+{
+ fprintf(stderr, "%s: %s\n", what, elem->origin ? elem->origin : "()");
+}
+static void debug_set(const char *what, const char *match, struct git_attr *attr, void *v)
+{
+ const char *value = v;
+
+ if (ATTR_TRUE(value))
+ value = "set";
+ else if (ATTR_FALSE(value))
+ value = "unset";
+ else if (ATTR_UNSET(value))
+ value = "unspecified";
+
+ fprintf(stderr, "%s: %s => %s (%s)\n",
+ what, attr->name, (char *) value, match);
+}
+#define debug_push(a) debug_info("push", (a))
+#define debug_pop(a) debug_info("pop", (a))
+#else
+#define debug_push(a) do { ; } while (0)
+#define debug_pop(a) do { ; } while (0)
+#define debug_set(a,b,c,d) do { ; } while (0)
+#endif
+
+static void bootstrap_attr_stack(void)
+{
+ if (!attr_stack) {
+ struct attr_stack *elem;
+
+ elem = read_attr_from_array(builtin_attr);
+ elem->origin = NULL;
+ elem->prev = attr_stack;
+ attr_stack = elem;
+
+ elem = read_attr_from_file(GITATTRIBUTES_FILE, 1);
+ elem->origin = strdup("");
+ elem->prev = attr_stack;
+ attr_stack = elem;
+ debug_push(elem);
+
+ elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE), 1);
+ elem->origin = NULL;
+ elem->prev = attr_stack;
+ attr_stack = elem;
+ }
+}
+
+static void prepare_attr_stack(const char *path, int dirlen)
+{
+ struct attr_stack *elem, *info;
+ int len;
+ char pathbuf[PATH_MAX];
+
+ /*
+ * At the bottom of the attribute stack is the built-in
+ * set of attribute definitions. Then, contents from
+ * .gitattribute files from directories closer to the
+ * root to the ones in deeper directories are pushed
+ * to the stack. Finally, at the very top of the stack
+ * we always keep the contents of $GIT_DIR/info/attributes.
+ *
+ * When checking, we use entries from near the top of the
+ * stack, preferring $GIT_DIR/info/attributes, then
+ * .gitattributes in deeper directories to shallower ones,
+ * and finally use the built-in set as the default.
+ */
+ if (!attr_stack)
+ bootstrap_attr_stack();
+
+ /*
+ * Pop the "info" one that is always at the top of the stack.
+ */
+ info = attr_stack;
+ attr_stack = info->prev;
+
+ /*
+ * Pop the ones from directories that are not the prefix of
+ * the path we are checking.
+ */
+ while (attr_stack && attr_stack->origin) {
+ int namelen = strlen(attr_stack->origin);
+
+ elem = attr_stack;
+ if (namelen <= dirlen &&
+ !strncmp(elem->origin, path, namelen))
+ break;
+
+ debug_pop(elem);
+ attr_stack = elem->prev;
+ free_attr_elem(elem);
+ }
+
+ /*
+ * Read from parent directories and push them down
+ */
+ while (1) {
+ char *cp;
+
+ len = strlen(attr_stack->origin);
+ if (dirlen <= len)
+ break;
+ memcpy(pathbuf, path, dirlen);
+ memcpy(pathbuf + dirlen, "/", 2);
+ cp = strchr(pathbuf + len + 1, '/');
+ strcpy(cp + 1, GITATTRIBUTES_FILE);
+ elem = read_attr_from_file(pathbuf, 0);
+ *cp = '\0';
+ elem->origin = strdup(pathbuf);
+ elem->prev = attr_stack;
+ attr_stack = elem;
+ debug_push(elem);
+ }
+
+ /*
+ * Finally push the "info" one at the top of the stack.
+ */
+ info->prev = attr_stack;
+ attr_stack = info;
+}
+
+static int path_matches(const char *pathname, int pathlen,
+ const char *pattern,
+ const char *base, int baselen)
+{
+ if (!strchr(pattern, '/')) {
+ /* match basename */
+ const char *basename = strrchr(pathname, '/');
+ basename = basename ? basename + 1 : pathname;
+ return (fnmatch(pattern, basename, 0) == 0);
+ }
+ /*
+ * match with FNM_PATHNAME; the pattern has base implicitly
+ * in front of it.
+ */
+ if (*pattern == '/')
+ pattern++;
+ if (pathlen < baselen ||
+ (baselen && pathname[baselen - 1] != '/') ||
+ strncmp(pathname, base, baselen))
+ return 0;
+ return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0;
+}
+
+static int fill_one(const char *what, struct match_attr *a, int rem)
+{
+ struct git_attr_check *check = check_all_attr;
+ int i;
+
+ for (i = 0; 0 < rem && i < a->num_attr; i++) {
+ struct git_attr *attr = a->state[i].attr;
+ const char **n = &(check[attr->attr_nr].value);
+ const char *v = a->state[i].setto;
+
+ if (*n == ATTR__UNKNOWN) {
+ debug_set(what, a->u.pattern, attr, v);
+ *n = v;
+ rem--;
+ }
+ }
+ return rem;
+}
+
+static int fill(const char *path, int pathlen, struct attr_stack *stk, int rem)
+{
+ int i;
+ const char *base = stk->origin ? stk->origin : "";
+
+ for (i = stk->num_matches - 1; 0 < rem && 0 <= i; i--) {
+ struct match_attr *a = stk->attrs[i];
+ if (a->is_macro)
+ continue;
+ if (path_matches(path, pathlen,
+ a->u.pattern, base, strlen(base)))
+ rem = fill_one("fill", a, rem);
+ }
+ return rem;
+}
+
+static int macroexpand(struct attr_stack *stk, int rem)
+{
+ int i;
+ struct git_attr_check *check = check_all_attr;
+
+ for (i = stk->num_matches - 1; 0 < rem && 0 <= i; i--) {
+ struct match_attr *a = stk->attrs[i];
+ if (!a->is_macro)
+ continue;
+ if (check[a->u.attr->attr_nr].value != ATTR__TRUE)
+ continue;
+ rem = fill_one("expand", a, rem);
+ }
+ return rem;
+}
+
+int git_checkattr(const char *path, int num, struct git_attr_check *check)
+{
+ struct attr_stack *stk;
+ const char *cp;
+ int dirlen, pathlen, i, rem;
+
+ bootstrap_attr_stack();
+ for (i = 0; i < attr_nr; i++)
+ check_all_attr[i].value = ATTR__UNKNOWN;
+
+ pathlen = strlen(path);
+ cp = strrchr(path, '/');
+ if (!cp)
+ dirlen = 0;
+ else
+ dirlen = cp - path;
+ prepare_attr_stack(path, dirlen);
+ rem = attr_nr;
+ for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
+ rem = fill(path, pathlen, stk, rem);
+
+ for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
+ rem = macroexpand(stk, rem);
+
+ for (i = 0; i < num; i++) {
+ const char *value = check_all_attr[check[i].attr->attr_nr].value;
+ if (value == ATTR__UNKNOWN)
+ value = ATTR__UNSET;
+ check[i].value = value;
+ }
+
+ return 0;
+}
diff --git a/attr.h b/attr.h
--- /dev/null
+++ b/attr.h
@@ -0,0 +1,34 @@
+#ifndef ATTR_H
+#define ATTR_H
+
+/* An attribute is a pointer to this opaque structure */
+struct git_attr;
+
+/*
+ * Given a string, return the gitattribute object that
+ * corresponds to it.
+ */
+struct git_attr *git_attr(const char *, int);
+
+/* Internal use */
+extern const char git_attr__true[];
+extern const char git_attr__false[];
+
+/* For public to check git_attr_check results */
+#define ATTR_TRUE(v) ((v) == git_attr__true)
+#define ATTR_FALSE(v) ((v) == git_attr__false)
+#define ATTR_UNSET(v) ((v) == NULL)
+
+/*
+ * Send one or more git_attr_check to git_checkattr(), and
+ * each 'value' member tells what its value is.
+ * Unset one is returned as NULL.
+ */
+struct git_attr_check {
+ struct git_attr *attr;
+ const char *value;
+};
+
+int git_checkattr(const char *path, int, struct git_attr_check *);
+
+#endif /* ATTR_H */
index 0a9ea417b8af4b06871eae8c47168dc178e9bb29..bd7d078e1ae5fe4ce0a16fda62a2c1743237941b 100644 (file)
--- a/blob.c
+++ b/blob.c
struct blob *lookup_blob(const unsigned char *sha1)
{
struct object *obj = lookup_object(sha1);
- if (!obj) {
- struct blob *ret = alloc_blob_node();
- created_object(sha1, &ret->object);
- ret->object.type = OBJ_BLOB;
- return ret;
- }
+ if (!obj)
+ return create_object(sha1, OBJ_BLOB, alloc_blob_node());
if (!obj->type)
obj->type = OBJ_BLOB;
if (obj->type != OBJ_BLOB) {
diff --git a/builtin-add.c b/builtin-add.c
index 9ec292590c49574dfd2a698772a406b5ccdc419e..5e6748f3566b17c7729576c0c42c61bcf0f7657d 100644 (file)
--- a/builtin-add.c
+++ b/builtin-add.c
#include "dir.h"
#include "exec_cmd.h"
#include "cache-tree.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "commit.h"
+#include "revision.h"
static const char builtin_add_usage[] =
-"git-add [-n] [-v] [-f] [--interactive | -i] [--] <filepattern>...";
+"git-add [-n] [-v] [-f] [--interactive | -i] [-u] [--] <filepattern>...";
+static int take_all_worktree_changes;
static const char *excludes_file;
static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
prune_directory(dir, pathspec, baselen);
}
+static void update_callback(struct diff_queue_struct *q,
+ struct diff_options *opt, void *cbdata)
+{
+ int i, verbose;
+
+ verbose = *((int *)cbdata);
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+ const char *path = p->one->path;
+ switch (p->status) {
+ default:
+ die("unexpacted diff status %c", p->status);
+ case DIFF_STATUS_UNMERGED:
+ case DIFF_STATUS_MODIFIED:
+ add_file_to_cache(path, verbose);
+ break;
+ case DIFF_STATUS_DELETED:
+ remove_file_from_cache(path);
+ if (verbose)
+ printf("remove '%s'\n", path);
+ break;
+ }
+ }
+}
+
+static void update_all(int verbose)
+{
+ struct rev_info rev;
+ init_revisions(&rev, "");
+ setup_revisions(0, NULL, &rev, NULL);
+ rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+ rev.diffopt.format_callback = update_callback;
+ rev.diffopt.format_callback_data = &verbose;
+ if (read_cache() < 0)
+ die("index file corrupt");
+ run_diff_files(&rev, 0);
+}
+
static int git_add_config(const char *var, const char *value)
{
if (!strcmp(var, "core.excludesfile")) {
verbose = 1;
continue;
}
+ if (!strcmp(arg, "-u")) {
+ take_all_worktree_changes = 1;
+ continue;
+ }
usage(builtin_add_usage);
}
+
+ if (take_all_worktree_changes) {
+ if (i < argc)
+ die("-u and explicit paths are incompatible");
+ update_all(verbose);
+ goto finish;
+ }
+
if (argc <= i) {
fprintf(stderr, "Nothing specified, nothing added.\n");
fprintf(stderr, "Maybe you wanted to say 'git add .'?\n");
for (i = 0; i < dir.nr; i++)
add_file_to_cache(dir.entries[i]->name, verbose);
+ finish:
if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
close(newfd) || commit_locked_index(&lock_file))
diff --git a/builtin-apply.c b/builtin-apply.c
index 94311e7af47f856c0ecfad08bebfde83a80938a2..f94d0dbf488ff43ccc496939560c81daf05772cf 100644 (file)
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -1475,8 +1475,8 @@ static int read_old_data(struct stat *st, const char *path, char **buf_p, unsign
}
close(fd);
nsize = got;
- nbuf = buf;
- if (convert_to_git(path, &nbuf, &nsize)) {
+ nbuf = convert_to_git(path, buf, &nsize);
+ if (nbuf) {
free(buf);
*buf_p = nbuf;
*alloc_p = nsize;
@@ -2355,9 +2355,8 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
{
- int fd, converted;
+ int fd;
char *nbuf;
- unsigned long nsize;
if (has_symlinks && S_ISLNK(mode))
/* Although buf:size is counted string, it also is NUL
@@ -2369,13 +2368,10 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
if (fd < 0)
return -1;
- nsize = size;
- nbuf = (char *) buf;
- converted = convert_to_working_tree(path, &nbuf, &nsize);
- if (converted) {
+ nbuf = convert_to_working_tree(path, buf, &size);
+ if (nbuf)
buf = nbuf;
- size = nsize;
- }
+
while (size) {
int written = xwrite(fd, buf, size);
if (written < 0)
@@ -2387,7 +2383,7 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
}
if (close(fd) < 0)
die("closing file %s: %s", path, strerror(errno));
- if (converted)
+ if (nbuf)
free(nbuf);
return 0;
}
diff --git a/builtin-cat-file.c b/builtin-cat-file.c
index d61d3d5b74ae23bb1ab10b23f518fa7a85ac5481..f132d583d3e2a2ac0fe696b66723c846902d0a19 100644 (file)
--- a/builtin-cat-file.c
+++ b/builtin-cat-file.c
void *buf;
unsigned long size;
int opt;
+ const char *exp_type, *obj_name;
git_config(git_default_config);
if (argc != 3)
usage("git-cat-file [-t|-s|-e|-p|<type>] <sha1>");
- if (get_sha1(argv[2], sha1))
- die("Not a valid object name %s", argv[2]);
+ exp_type = argv[1];
+ obj_name = argv[2];
+
+ if (get_sha1(obj_name, sha1))
+ die("Not a valid object name %s", obj_name);
opt = 0;
- if ( argv[1][0] == '-' ) {
- opt = argv[1][1];
- if ( !opt || argv[1][2] )
+ if ( exp_type[0] == '-' ) {
+ opt = exp_type[1];
+ if ( !opt || exp_type[2] )
opt = -1; /* Not a single character option */
}
case 'p':
type = sha1_object_info(sha1, NULL);
if (type < 0)
- die("Not a valid object name %s", argv[2]);
+ die("Not a valid object name %s", obj_name);
/* custom pretty-print here */
- if (type == OBJ_TREE)
- return cmd_ls_tree(2, argv + 1, NULL);
+ if (type == OBJ_TREE) {
+ const char *ls_args[3] = {"ls-tree", obj_name, NULL};
+ return cmd_ls_tree(2, ls_args, NULL);
+ }
buf = read_sha1_file(sha1, &type, &size);
if (!buf)
- die("Cannot read object %s", argv[2]);
+ die("Cannot read object %s", obj_name);
if (type == OBJ_TAG) {
pprint_tag(sha1, buf, size);
return 0;
/* otherwise just spit out the data */
break;
case 0:
- buf = read_object_with_reference(sha1, argv[1], &size, NULL);
+ buf = read_object_with_reference(sha1, exp_type, &size, NULL);
break;
default:
- die("git-cat-file: unknown option: %s\n", argv[1]);
+ die("git-cat-file: unknown option: %s\n", exp_type);
}
if (!buf)
- die("git-cat-file %s: bad file", argv[2]);
+ die("git-cat-file %s: bad file", obj_name);
write_or_die(1, buf, size);
return 0;
diff --git a/builtin-check-attr.c b/builtin-check-attr.c
--- /dev/null
+++ b/builtin-check-attr.c
@@ -0,0 +1,59 @@
+#include "builtin.h"
+#include "attr.h"
+#include "quote.h"
+
+static const char check_attr_usage[] =
+"git-check-attr attr... [--] pathname...";
+
+int cmd_check_attr(int argc, const char **argv, const char *prefix)
+{
+ struct git_attr_check *check;
+ int cnt, i, doubledash;
+
+ doubledash = -1;
+ for (i = 1; doubledash < 0 && i < argc; i++) {
+ if (!strcmp(argv[i], "--"))
+ doubledash = i;
+ }
+
+ /* If there is no double dash, we handle only one attribute */
+ if (doubledash < 0) {
+ cnt = 1;
+ doubledash = 1;
+ } else
+ cnt = doubledash - 1;
+ doubledash++;
+
+ if (cnt <= 0 || argc < doubledash)
+ usage(check_attr_usage);
+ check = xcalloc(cnt, sizeof(*check));
+ for (i = 0; i < cnt; i++) {
+ const char *name;
+ struct git_attr *a;
+ name = argv[i + 1];
+ a = git_attr(name, strlen(name));
+ if (!a)
+ return error("%s: not a valid attribute name", name);
+ check[i].attr = a;
+ }
+
+ for (i = doubledash; i < argc; i++) {
+ int j;
+ if (git_checkattr(argv[i], cnt, check))
+ die("git_checkattr died");
+ for (j = 0; j < cnt; j++) {
+ const char *value = check[j].value;
+
+ if (ATTR_TRUE(value))
+ value = "set";
+ else if (ATTR_FALSE(value))
+ value = "unset";
+ else if (ATTR_UNSET(value))
+ value = "unspecified";
+
+ write_name_quoted("", 0, argv[i], 1, stdout);
+ printf(": %s: %s\n", argv[j+1], value);
+ }
+ }
+ return 0;
+}
index 6263d8af295a5abce3e417157af7cb41e3623f38..ff90ebd465002882781507ecfcfc33cab2f759fc 100644 (file)
--- a/builtin-count-objects.c
+++ b/builtin-count-objects.c
for (p = packed_git; p; p = p->next) {
if (!p->pack_local)
continue;
- packed += num_packed_objects(p);
+ packed += p->num_objects;
num_pack++;
}
printf("count: %lu\n", loose);
diff --git a/builtin-diff.c b/builtin-diff.c
index 21d13f0b30359295b8385754fccb4bb71f995dba..2ae60097b8cfddd873a38f3988185d9fde77f511 100644 (file)
--- a/builtin-diff.c
+++ b/builtin-diff.c
if (diff_setup_done(&rev.diffopt) < 0)
die("diff_setup_done failed");
}
+ rev.diffopt.allow_external = 1;
/* Do we have --cached and not have a pending object, then
* default to HEAD by hand. Eek.
diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c
index e9d676455078b40138ad4716a912c4b2b5e10c2e..3145c01f7e0e81bf28314b92737ccf2abb129dcd 100644 (file)
--- a/builtin-fetch--tool.c
+++ b/builtin-fetch--tool.c
return 0;
}
+static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_result)
+{
+ int err = 0;
+ int lrr_count = lrr_count, i, pass;
+ const char *cp;
+ struct lrr {
+ const char *line;
+ const char *name;
+ int namelen;
+ int shown;
+ } *lrr_list = lrr_list;
+
+ for (pass = 0; pass < 2; pass++) {
+ /* pass 0 counts and allocates, pass 1 fills... */
+ cp = ls_remote_result;
+ i = 0;
+ while (1) {
+ const char *np;
+ while (*cp && isspace(*cp))
+ cp++;
+ if (!*cp)
+ break;
+ np = strchr(cp, '\n');
+ if (!np)
+ np = cp + strlen(cp);
+ if (pass) {
+ lrr_list[i].line = cp;
+ lrr_list[i].name = cp + 41;
+ lrr_list[i].namelen = np - (cp + 41);
+ }
+ i++;
+ cp = np;
+ }
+ if (!pass) {
+ lrr_count = i;
+ lrr_list = xcalloc(lrr_count, sizeof(*lrr_list));
+ }
+ }
+
+ while (1) {
+ const char *next;
+ int rreflen;
+ int i;
+
+ while (*rref && isspace(*rref))
+ rref++;
+ if (!*rref)
+ break;
+ next = strchr(rref, '\n');
+ if (!next)
+ next = rref + strlen(rref);
+ rreflen = next - rref;
+
+ for (i = 0; i < lrr_count; i++) {
+ struct lrr *lrr = &(lrr_list[i]);
+
+ if (rreflen == lrr->namelen &&
+ !memcmp(lrr->name, rref, rreflen)) {
+ if (!lrr->shown)
+ printf("%.*s\n",
+ sha1_only ? 40 : lrr->namelen + 41,
+ lrr->line);
+ lrr->shown = 1;
+ break;
+ }
+ }
+ if (lrr_count <= i) {
+ error("pick-rref: %.*s not found", rreflen, rref);
+ err = 1;
+ }
+ rref = next;
+ }
+ free(lrr_list);
+ return err;
+}
+
int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
{
int verbose = 0;
int force = 0;
+ int sopt = 0;
while (1 < argc) {
const char *arg = argv[1];
verbose = 1;
else if (!strcmp("-f", arg))
force = 1;
+ else if (!strcmp("-s", arg))
+ sopt = 1;
else
break;
argc--;
reflist = get_stdin();
return parse_reflist(reflist);
}
+ if (!strcmp("pick-rref", argv[1])) {
+ const char *ls_remote_result;
+ if (argc != 4)
+ return error("pick-rref takes 2 args");
+ ls_remote_result = argv[3];
+ if (!strcmp(ls_remote_result, "-"))
+ ls_remote_result = get_stdin();
+ return pick_rref(sopt, argv[2], ls_remote_result);
+ }
if (!strcmp("expand-refs-wildcard", argv[1])) {
const char *reflist;
if (argc < 4)
diff --git a/builtin-fsck.c b/builtin-fsck.c
index 05d98d2cfc0c8ba5e38482a181fee8115e9bd640..fcb8ed5af1bc50df19a2f533989aa9673ea166e8 100644 (file)
--- a/builtin-fsck.c
+++ b/builtin-fsck.c
case S_IFREG | 0644:
case S_IFLNK:
case S_IFDIR:
+ case S_IFDIRLNK:
break;
/*
* This is nonstandard, but we had a few of these
verify_pack(p, 0);
for (p = packed_git; p; p = p->next) {
- uint32_t i, num = num_packed_objects(p);
+ uint32_t i, num = p->num_objects;
for (i = 0; i < num; i++)
fsck_sha1(nth_packed_object_sha1(p, i));
}
int i;
read_cache();
for (i = 0; i < active_nr; i++) {
- struct blob *blob = lookup_blob(active_cache[i]->sha1);
+ unsigned int mode;
+ struct blob *blob;
struct object *obj;
+
+ mode = ntohl(active_cache[i]->ce_mode);
+ if (S_ISDIRLNK(mode))
+ continue;
+ blob = lookup_blob(active_cache[i]->sha1);
if (!blob)
continue;
obj = &blob->object;
diff --git a/builtin-ls-files.c b/builtin-ls-files.c
index 74a6acacc15b416a6149a519f3a69c5facfa5067..f7c066b24b7a6a728fd2f0bf4a92a31fb4a695dd 100644 (file)
--- a/builtin-ls-files.c
+++ b/builtin-ls-files.c
static void show_other_files(struct dir_struct *dir)
{
int i;
+
+
+ /*
+ * Skip matching and unmerged entries for the paths,
+ * since we want just "others".
+ *
+ * (Matching entries are normally pruned during
+ * the directory tree walk, but will show up for
+ * gitlinks because we don't necessarily have
+ * dir->show_other_directories set to suppress
+ * them).
+ */
for (i = 0; i < dir->nr; i++) {
- /* We should not have a matching entry, but we
- * may have an unmerged entry for this path.
- */
struct dir_entry *ent = dir->entries[i];
- int pos = cache_name_pos(ent->name, ent->len);
+ int len, pos;
struct cache_entry *ce;
+
+ /*
+ * Remove the '/' at the end that directory
+ * walking adds for directory entries.
+ */
+ len = ent->len;
+ if (len && ent->name[len-1] == '/')
+ len--;
+ pos = cache_name_pos(ent->name, len);
if (0 <= pos)
- die("bug in show-other-files");
+ continue; /* exact match */
pos = -pos - 1;
if (pos < active_nr) {
ce = active_cache[pos];
- if (ce_namelen(ce) == ent->len &&
- !memcmp(ce->name, ent->name, ent->len))
+ if (ce_namelen(ce) == len &&
+ !memcmp(ce->name, ent->name, len))
continue; /* Yup, this one exists unmerged */
}
show_dir_entry(tag_other, ent);
diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c
index 6472610ac2fecb8096ecab8fe29331a6fd6c009b..1cb4dca277b511315d3b914239c57621fc60bcf3 100644 (file)
--- a/builtin-ls-tree.c
+++ b/builtin-ls-tree.c
#include "cache.h"
#include "blob.h"
#include "tree.h"
+#include "commit.h"
#include "quote.h"
#include "builtin.h"
int retval = 0;
const char *type = blob_type;
- if (S_ISDIR(mode)) {
+ if (S_ISDIRLNK(mode)) {
+ /*
+ * Maybe we want to have some recursive version here?
+ *
+ * Something like:
+ *
+ if (show_subprojects(base, baselen, pathname)) {
+ if (fork()) {
+ chdir(base);
+ exec ls-tree;
+ }
+ waitpid();
+ }
+ *
+ * ..or similar..
+ */
+ type = commit_type;
+ } else if (S_ISDIR(mode)) {
if (show_recursive(base, baselen, pathname)) {
retval = READ_TREE_RECURSIVE;
if (!(ls_options & LS_SHOW_TREES))
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 45ac3e482acc1c0f8f2bad043f1ba50019dec505..b82762767013195fb58fe552296e61e951d92dbe 100644 (file)
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
#include "diff.h"
#include "revision.h"
#include "list-objects.h"
+#include "progress.h"
static const char pack_usage[] = "\
git-pack-objects [{ -q | --progress | --all-progress }] \n\
struct object_entry {
unsigned char sha1[20];
+ uint32_t crc32; /* crc of raw pack data for this object */
+ off_t offset; /* offset into the final pack file */
unsigned long size; /* uncompressed size */
- off_t offset; /* offset into the final pack file;
- * nonzero if already written.
- */
- unsigned int depth; /* delta depth */
- unsigned int delta_limit; /* base adjustment for in-pack delta */
unsigned int hash; /* name hint hash */
- enum object_type type;
- enum object_type in_pack_type; /* could be delta */
- unsigned long delta_size; /* delta data size (uncompressed) */
-#define in_pack_header_size delta_size /* only when reusing pack data */
- struct object_entry *delta; /* delta base object */
+ 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 */
struct object_entry *delta_child; /* deltified objects who bases me */
struct object_entry *delta_sibling; /* other deltified objects who
* uses the same base as me
*/
- int preferred_base; /* we do not pack this, but is encouraged to
- * be used as the base objectto delta huge
- * objects against.
- */
+ unsigned long delta_size; /* delta data size (uncompressed) */
+ enum object_type type;
+ enum object_type in_pack_type; /* could be delta */
+ unsigned char in_pack_header_size;
+ unsigned char preferred_base; /* we do not pack this, but is available
+ * to be used as the base objectto delta
+ * objects against.
+ */
};
/*
* expanded). nr_objects & nr_alloc controls this array. They are stored
* in the order we see -- typically rev-list --objects order that gives us
* nice "minimum seek" order.
- *
- * sorted-by-sha ans sorted-by-type are arrays of pointers that point at
- * elements in the objects array. The former is used to build the pack
- * index (lists object names in the ascending order to help offset lookup),
- * and the latter is used to group similar things together by try_delta()
- * heuristics.
*/
+static struct object_entry *objects;
+static uint32_t nr_objects, nr_alloc, nr_result;
-static unsigned char object_list_sha1[20];
static int non_empty;
static int no_reuse_delta;
static int local;
static int incremental;
static int allow_ofs_delta;
-
-static struct object_entry **sorted_by_sha, **sorted_by_type;
-static struct object_entry *objects;
-static uint32_t nr_objects, nr_alloc, nr_result;
-static const char *base_name;
+static const char *pack_tmp_name, *idx_tmp_name;
+static char tmpname[PATH_MAX];
static unsigned char pack_file_sha1[20];
static int progress = 1;
-static volatile sig_atomic_t progress_update;
static int window = 10;
static int pack_to_stdout;
static int num_preferred_base;
+static struct progress progress_state;
/*
* The object names in objects array are hashed with this hashtable,
- * to help looking up the entry by object name. Binary search from
- * sorted_by_sha is also possible but this was easier to code and faster.
+ * to help looking up the entry by object name.
* This hashtable is built after all the objects are seen.
*/
static int *object_ix;
static void prepare_pack_revindex(struct pack_revindex *rix)
{
struct packed_git *p = rix->p;
- int num_ent = num_packed_objects(p);
+ int num_ent = p->num_objects;
int i;
const char *index = p->index_data;
- index += 4 * 256;
rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1));
- for (i = 0; i < num_ent; i++) {
- uint32_t hl = *((uint32_t *)(index + 24 * i));
- rix->revindex[i].offset = ntohl(hl);
- rix->revindex[i].nr = i;
+ index += 4 * 256;
+
+ if (p->index_version > 1) {
+ const uint32_t *off_32 =
+ (uint32_t *)(index + 8 + p->num_objects * (20 + 4));
+ const uint32_t *off_64 = off_32 + p->num_objects;
+ for (i = 0; i < num_ent; i++) {
+ uint32_t off = ntohl(*off_32++);
+ if (!(off & 0x80000000)) {
+ rix->revindex[i].offset = off;
+ } else {
+ rix->revindex[i].offset =
+ ((uint64_t)ntohl(*off_64++)) << 32;
+ rix->revindex[i].offset |=
+ ntohl(*off_64++);
+ }
+ rix->revindex[i].nr = i;
+ }
+ } else {
+ for (i = 0; i < num_ent; i++) {
+ uint32_t hl = *((uint32_t *)(index + 24 * i));
+ rix->revindex[i].offset = ntohl(hl);
+ rix->revindex[i].nr = i;
+ }
}
+
/* This knows the pack format -- the 20-byte trailer
* follows immediately after the last object data.
*/
prepare_pack_revindex(rix);
revindex = rix->revindex;
lo = 0;
- hi = num_packed_objects(p) + 1;
+ hi = p->num_objects + 1;
do {
int mi = (lo + hi) / 2;
if (revindex[mi].offset == ofs) {
die("internal error: pack revindex corrupt");
}
-static off_t find_packed_object_size(struct packed_git *p, off_t ofs)
-{
- struct revindex_entry *entry = find_packed_object(p, ofs);
- return entry[1].offset - ofs;
-}
-
static const unsigned char *find_packed_object_name(struct packed_git *p,
off_t ofs)
{
stream.total_in == len) ? 0 : -1;
}
+static int check_pack_crc(struct packed_git *p, struct pack_window **w_curs,
+ off_t offset, off_t len, unsigned int nr)
+{
+ const uint32_t *index_crc;
+ uint32_t data_crc = crc32(0, Z_NULL, 0);
+
+ do {
+ unsigned int avail;
+ void *data = use_pack(p, w_curs, offset, &avail);
+ if (avail > len)
+ avail = len;
+ data_crc = crc32(data_crc, data, avail);
+ offset += avail;
+ len -= avail;
+ } while (len);
+
+ index_crc = p->index_data;
+ index_crc += 2 + 256 + p->num_objects * (20/4) + nr;
+
+ return data_crc != ntohl(*index_crc);
+}
+
static void copy_pack_data(struct sha1file *f,
struct packed_git *p,
struct pack_window **w_curs,
return check_loose_inflate(map, mapsize, size);
}
-static off_t write_object(struct sha1file *f,
+static unsigned long write_object(struct sha1file *f,
struct object_entry *entry)
{
unsigned long size;
enum object_type obj_type;
int to_reuse = 0;
+ if (!pack_to_stdout)
+ crc32_begin(f);
+
obj_type = entry->type;
if (! entry->in_pack)
to_reuse = 0; /* can't reuse what we don't have */
else {
struct packed_git *p = entry->in_pack;
struct pack_window *w_curs = NULL;
+ struct revindex_entry *revidx;
off_t offset;
if (entry->delta) {
hdrlen += 20;
}
- offset = entry->in_pack_offset + entry->in_pack_header_size;
- datalen = find_packed_object_size(p, entry->in_pack_offset)
- - entry->in_pack_header_size;
- if (!pack_to_stdout && check_pack_inflate(p, &w_curs,
- offset, datalen, entry->size))
- die("corrupt delta in pack %s", sha1_to_hex(entry->sha1));
+ offset = entry->in_pack_offset;
+ revidx = find_packed_object(p, offset);
+ datalen = revidx[1].offset - offset;
+ if (!pack_to_stdout && p->index_version > 1 &&
+ check_pack_crc(p, &w_curs, offset, datalen, revidx->nr))
+ die("bad packed object CRC for %s", sha1_to_hex(entry->sha1));
+ offset += entry->in_pack_header_size;
+ datalen -= entry->in_pack_header_size;
+ if (!pack_to_stdout && p->index_version == 1 &&
+ check_pack_inflate(p, &w_curs, offset, datalen, entry->size))
+ die("corrupt packed object for %s", sha1_to_hex(entry->sha1));
copy_pack_data(f, p, &w_curs, offset, datalen);
unuse_pack(&w_curs);
reused++;
if (entry->delta)
written_delta++;
written++;
+ if (!pack_to_stdout)
+ entry->crc32 = crc32_end(f);
return hdrlen + datalen;
}
struct object_entry *e,
off_t offset)
{
+ unsigned long size;
+
+ /* offset is non zero if object is written already. */
if (e->offset || e->preferred_base)
- /* offset starts from header size and cannot be zero
- * if it is written already.
- */
return offset;
- /* if we are deltified, write out its base object first. */
+
+ /* if we are deltified, write out base object first. */
if (e->delta)
offset = write_one(f, e->delta, offset);
+
e->offset = offset;
- return offset + write_object(f, e);
+ size = write_object(f, e);
+
+ /* make sure off_t is sufficiently large not to wrap */
+ if (offset > offset + size)
+ die("pack too large for current definition of off_t");
+ return offset + size;
}
-static void write_pack_file(void)
+static off_t write_pack_file(void)
{
uint32_t i;
struct sha1file *f;
- off_t offset;
+ off_t offset, last_obj_offset = 0;
struct pack_header hdr;
- unsigned last_percent = 999;
int do_progress = progress;
- if (!base_name) {
+ if (pack_to_stdout) {
f = sha1fd(1, "<stdout>");
do_progress >>= 1;
+ } else {
+ int fd;
+ snprintf(tmpname, sizeof(tmpname), "tmp_pack_XXXXXX");
+ fd = mkstemp(tmpname);
+ if (fd < 0)
+ die("unable to create %s: %s\n", tmpname, strerror(errno));
+ pack_tmp_name = xstrdup(tmpname);
+ f = sha1fd(fd, pack_tmp_name);
}
- else
- f = sha1create("%s-%s.%s", base_name,
- sha1_to_hex(object_list_sha1), "pack");
+
if (do_progress)
- fprintf(stderr, "Writing %u objects.\n", nr_result);
+ start_progress(&progress_state, "Writing %u objects...", "", nr_result);
hdr.hdr_signature = htonl(PACK_SIGNATURE);
hdr.hdr_version = htonl(PACK_VERSION);
if (!nr_result)
goto done;
for (i = 0; i < nr_objects; i++) {
+ last_obj_offset = offset;
offset = write_one(f, objects + i, offset);
- if (do_progress) {
- unsigned percent = written * 100 / nr_result;
- if (progress_update || percent != last_percent) {
- fprintf(stderr, "%4u%% (%u/%u) done\r",
- percent, written, nr_result);
- progress_update = 0;
- last_percent = percent;
- }
- }
+ if (do_progress)
+ display_progress(&progress_state, written);
}
if (do_progress)
- fputc('\n', stderr);
+ stop_progress(&progress_state);
done:
if (written != nr_result)
die("wrote %u objects while expecting %u", written, nr_result);
sha1close(f, pack_file_sha1, 1);
+
+ return last_obj_offset;
}
-static void write_index_file(void)
+static int sha1_sort(const void *_a, const void *_b)
{
- uint32_t i;
- struct sha1file *f = sha1create("%s-%s.%s", base_name,
- sha1_to_hex(object_list_sha1), "idx");
- struct object_entry **list = sorted_by_sha;
- struct object_entry **last = list + nr_result;
+ const struct object_entry *a = *(struct object_entry **)_a;
+ const struct object_entry *b = *(struct object_entry **)_b;
+ return hashcmp(a->sha1, b->sha1);
+}
+
+static uint32_t index_default_version = 1;
+static uint32_t index_off32_limit = 0x7fffffff;
+
+static void write_index_file(off_t last_obj_offset, unsigned char *sha1)
+{
+ struct sha1file *f;
+ struct object_entry **sorted_by_sha, **list, **last;
uint32_t array[256];
+ uint32_t i, index_version;
+ SHA_CTX ctx;
+ int fd;
+
+ snprintf(tmpname, sizeof(tmpname), "tmp_idx_XXXXXX");
+ fd = mkstemp(tmpname);
+ if (fd < 0)
+ die("unable to create %s: %s\n", tmpname, strerror(errno));
+ idx_tmp_name = xstrdup(tmpname);
+ f = sha1fd(fd, idx_tmp_name);
+
+ if (nr_result) {
+ uint32_t j = 0;
+ sorted_by_sha =
+ xcalloc(nr_result, sizeof(struct object_entry *));
+ for (i = 0; i < nr_objects; i++)
+ if (!objects[i].preferred_base)
+ sorted_by_sha[j++] = objects + i;
+ if (j != nr_result)
+ die("listed %u objects while expecting %u", j, nr_result);
+ qsort(sorted_by_sha, nr_result, sizeof(*sorted_by_sha), sha1_sort);
+ list = sorted_by_sha;
+ last = sorted_by_sha + nr_result;
+ } else
+ sorted_by_sha = list = last = NULL;
+
+ /* if last object's offset is >= 2^31 we should use index V2 */
+ index_version = (last_obj_offset >> 31) ? 2 : index_default_version;
+
+ /* index versions 2 and above need a header */
+ if (index_version >= 2) {
+ struct pack_idx_header hdr;
+ hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
+ hdr.idx_version = htonl(index_version);
+ sha1write(f, &hdr, sizeof(hdr));
+ }
/*
* Write the first-level table (the list is sorted,
}
sha1write(f, array, 256 * 4);
- /*
- * Write the actual SHA1 entries..
- */
+ /* Compute the SHA1 hash of sorted object names. */
+ SHA1_Init(&ctx);
+
+ /* Write the actual SHA1 entries. */
list = sorted_by_sha;
for (i = 0; i < nr_result; i++) {
struct object_entry *entry = *list++;
- uint32_t offset = htonl(entry->offset);
- sha1write(f, &offset, 4);
+ if (index_version < 2) {
+ uint32_t offset = htonl(entry->offset);
+ sha1write(f, &offset, 4);
+ }
sha1write(f, entry->sha1, 20);
+ SHA1_Update(&ctx, entry->sha1, 20);
+ }
+
+ if (index_version >= 2) {
+ unsigned int nr_large_offset = 0;
+
+ /* write the crc32 table */
+ list = sorted_by_sha;
+ for (i = 0; i < nr_objects; i++) {
+ struct object_entry *entry = *list++;
+ uint32_t crc32_val = htonl(entry->crc32);
+ sha1write(f, &crc32_val, 4);
+ }
+
+ /* write the 32-bit offset table */
+ list = sorted_by_sha;
+ for (i = 0; i < nr_objects; i++) {
+ struct object_entry *entry = *list++;
+ uint32_t offset = (entry->offset <= index_off32_limit) ?
+ entry->offset : (0x80000000 | nr_large_offset++);
+ offset = htonl(offset);
+ sha1write(f, &offset, 4);
+ }
+
+ /* write the large offset table */
+ list = sorted_by_sha;
+ while (nr_large_offset) {
+ struct object_entry *entry = *list++;
+ uint64_t offset = entry->offset;
+ if (offset > index_off32_limit) {
+ uint32_t split[2];
+ split[0] = htonl(offset >> 32);
+ split[1] = htonl(offset & 0xffffffff);
+ sha1write(f, split, 8);
+ nr_large_offset--;
+ }
+ }
}
+
sha1write(f, pack_file_sha1, 20);
sha1close(f, NULL, 1);
+ free(sorted_by_sha);
+ SHA1_Final(sha1, &ctx);
}
static int locate_object_entry_hash(const unsigned char *sha1)
return hash;
}
-static int add_object_entry(const unsigned char *sha1, unsigned hash, int exclude)
+static int add_object_entry(const unsigned char *sha1, enum object_type type,
+ unsigned hash, int exclude)
{
- uint32_t idx = nr_objects;
struct object_entry *entry;
- struct packed_git *p;
+ struct packed_git *p, *found_pack = NULL;
off_t found_offset = 0;
- struct packed_git *found_pack = NULL;
- int ix, status = 0;
-
- if (!exclude) {
- for (p = packed_git; p; p = p->next) {
- off_t offset = find_pack_entry_one(sha1, p);
- if (offset) {
- if (incremental)
- return 0;
- if (local && !p->pack_local)
- return 0;
- if (!found_pack) {
- found_offset = offset;
- found_pack = p;
- }
+ int ix;
+
+ ix = nr_objects ? locate_object_entry_hash(sha1) : -1;
+ if (ix >= 0) {
+ if (exclude) {
+ entry = objects + object_ix[ix] - 1;
+ if (!entry->preferred_base)
+ nr_result--;
+ entry->preferred_base = 1;
+ }
+ return 0;
+ }
+
+ for (p = packed_git; p; p = p->next) {
+ off_t offset = find_pack_entry_one(sha1, p);
+ if (offset) {
+ if (!found_pack) {
+ found_offset = offset;
+ found_pack = p;
}
+ if (exclude)
+ break;
+ if (incremental)
+ return 0;
+ if (local && !p->pack_local)
+ return 0;
}
}
- if ((entry = locate_object_entry(sha1)) != NULL)
- goto already_added;
- if (idx >= nr_alloc) {
- nr_alloc = (idx + 1024) * 3 / 2;
+ if (nr_objects >= nr_alloc) {
+ nr_alloc = (nr_alloc + 1024) * 3 / 2;
objects = xrealloc(objects, nr_alloc * sizeof(*entry));
}
- entry = objects + idx;
- nr_objects = idx + 1;
+
+ entry = objects + nr_objects++;
memset(entry, 0, sizeof(*entry));
hashcpy(entry->sha1, sha1);
entry->hash = hash;
+ if (type)
+ entry->type = type;
+ if (exclude)
+ entry->preferred_base = 1;
+ else
+ nr_result++;
+ if (found_pack) {
+ entry->in_pack = found_pack;
+ entry->in_pack_offset = found_offset;
+ }
if (object_ix_hashsz * 3 <= nr_objects * 4)
rehash_objects();
- else {
- ix = locate_object_entry_hash(entry->sha1);
- if (0 <= ix)
- die("internal error in object hashing.");
- object_ix[-1 - ix] = idx + 1;
- }
- status = 1;
+ else
+ object_ix[-1 - ix] = nr_objects;
- already_added:
- if (progress_update) {
- fprintf(stderr, "Counting objects...%u\r", nr_objects);
- progress_update = 0;
- }
- if (exclude)
- entry->preferred_base = 1;
- else {
- if (found_pack) {
- entry->in_pack = found_pack;
- entry->in_pack_offset = found_offset;
- }
- }
- return status;
+ if (progress)
+ display_progress(&progress_state, nr_objects);
+
+ return 1;
}
struct pbase_tree_cache {
const char *fullname)
{
struct name_entry entry;
+ int cmp;
while (tree_entry(tree,&entry)) {
- unsigned long size;
- enum object_type type;
-
- if (tree_entry_len(entry.path, entry.sha1) != cmplen ||
- memcmp(entry.path, name, cmplen) ||
- !has_sha1_file(entry.sha1) ||
- (type = sha1_object_info(entry.sha1, &size)) < 0)
+ cmp = tree_entry_len(entry.path, entry.sha1) != cmplen ? 1 :
+ memcmp(name, entry.path, cmplen);
+ if (cmp > 0)
continue;
+ if (cmp < 0)
+ return;
if (name[cmplen] != '/') {
unsigned hash = name_hash(fullname);
- add_object_entry(entry.sha1, hash, 1);
+ add_object_entry(entry.sha1,
+ S_ISDIR(entry.mode) ? OBJ_TREE : OBJ_BLOB,
+ hash, 1);
return;
}
- if (type == OBJ_TREE) {
+ if (S_ISDIR(entry.mode)) {
struct tree_desc sub;
struct pbase_tree_cache *tree;
const char *down = name+cmplen+1;
static void add_preferred_base_object(const char *name, unsigned hash)
{
struct pbase_tree *it;
- int cmplen = name_cmp_len(name);
+ int cmplen;
- if (check_pbase_path(hash))
+ if (!num_preferred_base || check_pbase_path(hash))
return;
+ cmplen = name_cmp_len(name);
for (it = pbase_tree; it; it = it->next) {
if (cmplen == 0) {
- hash = name_hash("");
- add_object_entry(it->pcache.sha1, hash, 1);
+ add_object_entry(it->pcache.sha1, OBJ_TREE, 0, 1);
}
else {
struct tree_desc tree;
static void check_object(struct object_entry *entry)
{
- if (entry->in_pack && !entry->preferred_base) {
+ if (entry->in_pack) {
struct packed_git *p = entry->in_pack;
struct pack_window *w_curs = NULL;
- unsigned long size, used;
+ const unsigned char *base_ref = NULL;
+ struct object_entry *base_entry;
+ unsigned long used, used_0;
unsigned int avail;
- unsigned char *buf;
- struct object_entry *base_entry = NULL;
+ off_t ofs;
+ unsigned char *buf, c;
buf = use_pack(p, &w_curs, entry->in_pack_offset, &avail);
- /* We want in_pack_type even if we do not reuse delta.
+ /*
+ * We want in_pack_type even if we do not reuse delta.
* There is no point not reusing non-delta representations.
*/
used = unpack_object_header_gently(buf, avail,
- &entry->in_pack_type, &size);
+ &entry->in_pack_type,
+ &entry->size);
- /* Check if it is delta, and the base is also an object
- * we are going to pack. If so we will reuse the existing
- * delta.
+ /*
+ * Determine if this is a delta and if so whether we can
+ * reuse it or not. Otherwise let's find out as cheaply as
+ * possible what the actual type and size for this object is.
*/
- if (!no_reuse_delta) {
- unsigned char c;
- const unsigned char *base_name;
- off_t ofs;
- unsigned long used_0;
- /* there is at least 20 bytes left in the pack */
- switch (entry->in_pack_type) {
- case OBJ_REF_DELTA:
- base_name = use_pack(p, &w_curs,
- entry->in_pack_offset + used, NULL);
- used += 20;
- break;
- case OBJ_OFS_DELTA:
- buf = use_pack(p, &w_curs,
- entry->in_pack_offset + used, NULL);
- used_0 = 0;
- c = buf[used_0++];
- ofs = c & 127;
- while (c & 128) {
- ofs += 1;
- if (!ofs || ofs & ~(~0UL >> 7))
- die("delta base offset overflow in pack for %s",
- sha1_to_hex(entry->sha1));
- c = buf[used_0++];
- ofs = (ofs << 7) + (c & 127);
- }
- if (ofs >= entry->in_pack_offset)
- die("delta base offset out of bound for %s",
+ switch (entry->in_pack_type) {
+ default:
+ /* Not a delta hence we've already got all we need. */
+ entry->type = entry->in_pack_type;
+ entry->in_pack_header_size = used;
+ unuse_pack(&w_curs);
+ return;
+ case OBJ_REF_DELTA:
+ if (!no_reuse_delta && !entry->preferred_base)
+ base_ref = use_pack(p, &w_curs,
+ entry->in_pack_offset + used, NULL);
+ entry->in_pack_header_size = used + 20;
+ break;
+ case OBJ_OFS_DELTA:
+ buf = use_pack(p, &w_curs,
+ entry->in_pack_offset + used, NULL);
+ used_0 = 0;
+ c = buf[used_0++];
+ ofs = c & 127;
+ while (c & 128) {
+ ofs += 1;
+ if (!ofs || MSB(ofs, 7))
+ die("delta base offset overflow in pack for %s",
sha1_to_hex(entry->sha1));
- ofs = entry->in_pack_offset - ofs;
- base_name = find_packed_object_name(p, ofs);
- used += used_0;
- break;
- default:
- base_name = NULL;
+ c = buf[used_0++];
+ ofs = (ofs << 7) + (c & 127);
}
- if (base_name)
- base_entry = locate_object_entry(base_name);
+ if (ofs >= entry->in_pack_offset)
+ die("delta base offset out of bound for %s",
+ sha1_to_hex(entry->sha1));
+ ofs = entry->in_pack_offset - ofs;
+ if (!no_reuse_delta && !entry->preferred_base)
+ base_ref = find_packed_object_name(p, ofs);
+ entry->in_pack_header_size = used + used_0;
+ break;
}
- unuse_pack(&w_curs);
- entry->in_pack_header_size = used;
-
- if (base_entry) {
- /* Depth value does not matter - find_deltas()
- * will never consider reused delta as the
- * base object to deltify other objects
- * against, in order to avoid circular deltas.
+ if (base_ref && (base_entry = locate_object_entry(base_ref))) {
+ /*
+ * If base_ref was set above that means we wish to
+ * reuse delta data, and we even found that base
+ * in the list of objects we want to pack. Goodie!
+ *
+ * Depth value does not matter - find_deltas() will
+ * never consider reused delta as the base object to
+ * deltify other objects against, in order to avoid
+ * circular deltas.
*/
-
- /* uncompressed size of the delta data */
- entry->size = size;
- entry->delta = base_entry;
entry->type = entry->in_pack_type;
-
+ entry->delta = base_entry;
entry->delta_sibling = base_entry->delta_child;
base_entry->delta_child = entry;
+ unuse_pack(&w_curs);
+ return;
+ }
+ if (entry->type) {
+ /*
+ * This must be a delta and we already know what the
+ * final object type is. Let's extract the actual
+ * object size from the delta header.
+ */
+ entry->size = get_size_from_delta(p, &w_curs,
+ entry->in_pack_offset + entry->in_pack_header_size);
+ unuse_pack(&w_curs);
return;
}
- /* Otherwise we would do the usual */
+
+ /*
+ * No choice but to fall back to the recursive delta walk
+ * with sha1_object_info() to find about the object type
+ * at this point...
+ */
+ unuse_pack(&w_curs);
}
entry->type = sha1_object_info(entry->sha1, &entry->size);
sha1_to_hex(entry->sha1));
}
-static unsigned int check_delta_limit(struct object_entry *me, unsigned int n)
+static int pack_offset_sort(const void *_a, const void *_b)
{
- struct object_entry *child = me->delta_child;
- unsigned int m = n;
- while (child) {
- unsigned int c = check_delta_limit(child, n + 1);
- if (m < c)
- m = c;
- child = child->delta_sibling;
- }
- return m;
-}
+ const struct object_entry *a = *(struct object_entry **)_a;
+ const struct object_entry *b = *(struct object_entry **)_b;
-static void get_object_details(void)
-{
- uint32_t i;
- struct object_entry *entry;
+ /* avoid filesystem trashing with loose objects */
+ if (!a->in_pack && !b->in_pack)
+ return hashcmp(a->sha1, b->sha1);
- prepare_pack_ix();
- for (i = 0, entry = objects; i < nr_objects; i++, entry++)
- check_object(entry);
-
- if (nr_objects == nr_result) {
- /*
- * Depth of objects that depend on the entry -- this
- * is subtracted from depth-max to break too deep
- * delta chain because of delta data reusing.
- * However, we loosen this restriction when we know we
- * are creating a thin pack -- it will have to be
- * expanded on the other end anyway, so do not
- * artificially cut the delta chain and let it go as
- * deep as it wants.
- */
- for (i = 0, entry = objects; i < nr_objects; i++, entry++)
- if (!entry->delta && entry->delta_child)
- entry->delta_limit =
- check_delta_limit(entry, 1);
- }
-}
-
-typedef int (*entry_sort_t)(const struct object_entry *, const struct object_entry *);
-
-static entry_sort_t current_sort;
-
-static int sort_comparator(const void *_a, const void *_b)
-{
- struct object_entry *a = *(struct object_entry **)_a;
- struct object_entry *b = *(struct object_entry **)_b;
- return current_sort(a,b);
+ if (a->in_pack < b->in_pack)
+ return -1;
+ if (a->in_pack > b->in_pack)
+ return 1;
+ return a->in_pack_offset < b->in_pack_offset ? -1 :
+ (a->in_pack_offset > b->in_pack_offset);
}
-static struct object_entry **create_sorted_list(entry_sort_t sort)
+static void get_object_details(void)
{
- struct object_entry **list = xmalloc(nr_objects * sizeof(struct object_entry *));
uint32_t i;
+ struct object_entry **sorted_by_offset;
+ sorted_by_offset = xcalloc(nr_objects, sizeof(struct object_entry *));
for (i = 0; i < nr_objects; i++)
- list[i] = objects + i;
- current_sort = sort;
- qsort(list, nr_objects, sizeof(struct object_entry *), sort_comparator);
- return list;
-}
+ sorted_by_offset[i] = objects + i;
+ qsort(sorted_by_offset, nr_objects, sizeof(*sorted_by_offset), pack_offset_sort);
-static int sha1_sort(const struct object_entry *a, const struct object_entry *b)
-{
- return hashcmp(a->sha1, b->sha1);
+ prepare_pack_ix();
+ for (i = 0; i < nr_objects; i++)
+ check_object(sorted_by_offset[i]);
+ free(sorted_by_offset);
}
-static struct object_entry **create_final_object_list(void)
+static int type_size_sort(const void *_a, const void *_b)
{
- struct object_entry **list;
- uint32_t i, j;
-
- for (i = nr_result = 0; i < nr_objects; i++)
- if (!objects[i].preferred_base)
- nr_result++;
- list = xmalloc(nr_result * sizeof(struct object_entry *));
- for (i = j = 0; i < nr_objects; i++) {
- if (!objects[i].preferred_base)
- list[j++] = objects + i;
- }
- current_sort = sha1_sort;
- qsort(list, nr_result, sizeof(struct object_entry *), sort_comparator);
- return list;
-}
+ const struct object_entry *a = *(struct object_entry **)_a;
+ const struct object_entry *b = *(struct object_entry **)_b;
-static int type_size_sort(const struct object_entry *a, const struct object_entry *b)
-{
if (a->type < b->type)
return -1;
if (a->type > b->type)
@@ -1167,7 +1270,7 @@ static int type_size_sort(const struct object_entry *a, const struct object_entr
return -1;
if (a->size > b->size)
return 1;
- return a < b ? -1 : (a > b);
+ return a > b ? -1 : (a < b); /* newest last */
}
struct unpacked {
trg_entry->in_pack_type != OBJ_OFS_DELTA)
return 0;
- /*
- * If the current object is at pack edge, take the depth the
- * objects that depend on the current object into account --
- * otherwise they would become too deep.
- */
- if (trg_entry->delta_child) {
- if (max_depth <= trg_entry->delta_limit)
- return 0;
- max_depth -= trg_entry->delta_limit;
- }
+ /* Let's not bust the allowed depth. */
if (src_entry->depth >= max_depth)
return 0;
return 1;
}
-static void progress_interval(int signum)
+static unsigned int check_delta_limit(struct object_entry *me, unsigned int n)
{
- progress_update = 1;
+ struct object_entry *child = me->delta_child;
+ unsigned int m = n;
+ while (child) {
+ unsigned int c = check_delta_limit(child, n + 1);
+ if (m < c)
+ m = c;
+ child = child->delta_sibling;
+ }
+ return m;
}
static void find_deltas(struct object_entry **list, int window, int depth)
uint32_t i = nr_objects, idx = 0, processed = 0;
unsigned int array_size = window * sizeof(struct unpacked);
struct unpacked *array;
- unsigned last_percent = 999;
+ int max_depth;
if (!nr_objects)
return;
array = xmalloc(array_size);
memset(array, 0, array_size);
if (progress)
- fprintf(stderr, "Deltifying %u objects.\n", nr_result);
+ start_progress(&progress_state, "Deltifying %u objects...", "", nr_result);
do {
struct object_entry *entry = list[--i];
if (!entry->preferred_base)
processed++;
- if (progress) {
- unsigned percent = processed * 100 / nr_result;
- if (percent != last_percent || progress_update) {
- fprintf(stderr, "%4u%% (%u/%u) done\r",
- percent, processed, nr_result);
- progress_update = 0;
- last_percent = percent;
- }
- }
+ if (progress)
+ display_progress(&progress_state, processed);
if (entry->delta)
/* This happens if we decided to reuse existing
n->data = NULL;
n->entry = entry;
+ /*
+ * If the current object is at pack edge, take the depth the
+ * objects that depend on the current object into account
+ * otherwise they would become too deep.
+ */
+ max_depth = depth;
+ if (entry->delta_child) {
+ max_depth -= check_delta_limit(entry, 0);
+ if (max_depth <= 0)
+ goto next;
+ }
+
j = window;
while (--j > 0) {
uint32_t other_idx = idx + j;
m = array + other_idx;
if (!m->entry)
break;
- if (try_delta(n, m, depth) < 0)
+ if (try_delta(n, m, max_depth) < 0)
break;
}
+
/* if we made n a delta, and if n is already at max
* depth, leaving it in the window is pointless. we
* should evict it first.
if (entry->delta && depth <= entry->depth)
continue;
+ next:
idx++;
if (idx >= window)
idx = 0;
} while (i > 0);
if (progress)
- fputc('\n', stderr);
+ stop_progress(&progress_state);
for (i = 0; i < window; ++i) {
free_delta_index(array[i].index);
static void prepare_pack(int window, int depth)
{
- get_object_details();
- sorted_by_type = create_sorted_list(type_size_sort);
- if (window && depth)
- find_deltas(sorted_by_type, window+1, depth);
-}
-
-static int reuse_cached_pack(unsigned char *sha1)
-{
- static const char cache[] = "pack-cache/pack-%s.%s";
- char *cached_pack, *cached_idx;
- int ifd, ofd, ifd_ix = -1;
-
- cached_pack = git_path(cache, sha1_to_hex(sha1), "pack");
- ifd = open(cached_pack, O_RDONLY);
- if (ifd < 0)
- return 0;
-
- if (!pack_to_stdout) {
- cached_idx = git_path(cache, sha1_to_hex(sha1), "idx");
- ifd_ix = open(cached_idx, O_RDONLY);
- if (ifd_ix < 0) {
- close(ifd);
- return 0;
- }
- }
-
- if (progress)
- fprintf(stderr, "Reusing %u objects pack %s\n", nr_objects,
- sha1_to_hex(sha1));
+ struct object_entry **delta_list;
+ uint32_t i;
- if (pack_to_stdout) {
- if (copy_fd(ifd, 1))
- exit(1);
- close(ifd);
- }
- else {
- char name[PATH_MAX];
- snprintf(name, sizeof(name),
- "%s-%s.%s", base_name, sha1_to_hex(sha1), "pack");
- ofd = open(name, O_CREAT | O_EXCL | O_WRONLY, 0666);
- if (ofd < 0)
- die("unable to open %s (%s)", name, strerror(errno));
- if (copy_fd(ifd, ofd))
- exit(1);
- close(ifd);
-
- snprintf(name, sizeof(name),
- "%s-%s.%s", base_name, sha1_to_hex(sha1), "idx");
- ofd = open(name, O_CREAT | O_EXCL | O_WRONLY, 0666);
- if (ofd < 0)
- die("unable to open %s (%s)", name, strerror(errno));
- if (copy_fd(ifd_ix, ofd))
- exit(1);
- close(ifd_ix);
- puts(sha1_to_hex(sha1));
- }
+ get_object_details();
- return 1;
-}
+ if (!window || !depth)
+ return;
-static void setup_progress_signal(void)
-{
- struct sigaction sa;
- struct itimerval v;
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = progress_interval;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
- sigaction(SIGALRM, &sa, NULL);
-
- v.it_interval.tv_sec = 1;
- v.it_interval.tv_usec = 0;
- v.it_value = v.it_interval;
- setitimer(ITIMER_REAL, &v, NULL);
+ delta_list = xmalloc(nr_objects * sizeof(*delta_list));
+ for (i = 0; i < nr_objects; i++)
+ delta_list[i] = objects + i;
+ qsort(delta_list, nr_objects, sizeof(*delta_list), type_size_sort);
+ find_deltas(delta_list, window+1, depth);
+ free(delta_list);
}
static int git_pack_config(const char *k, const char *v)
hash = name_hash(line+41);
add_preferred_base_object(line+41, hash);
- add_object_entry(sha1, hash, 0);
+ add_object_entry(sha1, 0, hash, 0);
}
}
static void show_commit(struct commit *commit)
{
- unsigned hash = name_hash("");
- add_preferred_base_object("", hash);
- add_object_entry(commit->object.sha1, hash, 0);
+ add_object_entry(commit->object.sha1, OBJ_COMMIT, 0, 0);
}
static void show_object(struct object_array_entry *p)
{
unsigned hash = name_hash(p->name);
add_preferred_base_object(p->name, hash);
- add_object_entry(p->item->sha1, hash, 0);
+ add_object_entry(p->item->sha1, p->item->type, hash, 0);
}
static void show_edge(struct commit *commit)
traverse_commit_list(&revs, show_commit, show_object);
}
+static int adjust_perm(const char *path, mode_t mode)
+{
+ if (chmod(path, mode))
+ return -1;
+ return adjust_shared_perm(path);
+}
+
int cmd_pack_objects(int argc, const char **argv, const char *prefix)
{
- SHA_CTX ctx;
int depth = 10;
- struct object_entry **list;
int use_internal_rev_list = 0;
int thin = 0;
uint32_t i;
+ off_t last_obj_offset;
+ const char *base_name = NULL;
const char **rp_av;
int rp_ac_alloc = 64;
int rp_ac;
rp_av[1] = "--objects-edge";
continue;
}
+ if (!prefixcmp(arg, "--index-version=")) {
+ char *c;
+ index_default_version = strtoul(arg + 16, &c, 10);
+ if (index_default_version > 2)
+ die("bad %s", arg);
+ if (*c == ',')
+ index_off32_limit = strtoul(c+1, &c, 0);
+ if (*c || index_off32_limit & 0x80000000)
+ die("bad %s", arg);
+ continue;
+ }
usage(pack_usage);
}
prepare_packed_git();
- if (progress) {
- fprintf(stderr, "Generating pack...\n");
- setup_progress_signal();
- }
-
+ if (progress)
+ start_progress(&progress_state, "Generating pack...",
+ "Counting objects: ", 0);
if (!use_internal_rev_list)
read_object_list_from_stdin();
else {
rp_av[rp_ac] = NULL;
get_object_list(rp_ac, rp_av);
}
-
- if (progress)
+ if (progress) {
+ stop_progress(&progress_state);
fprintf(stderr, "Done counting %u objects.\n", nr_objects);
- sorted_by_sha = create_final_object_list();
+ }
+
if (non_empty && !nr_result)
return 0;
-
- SHA1_Init(&ctx);
- list = sorted_by_sha;
- for (i = 0; i < nr_result; i++) {
- struct object_entry *entry = *list++;
- SHA1_Update(&ctx, entry->sha1, 20);
- }
- SHA1_Final(object_list_sha1, &ctx);
if (progress && (nr_objects != nr_result))
fprintf(stderr, "Result has %u objects.\n", nr_result);
-
- if (reuse_cached_pack(object_list_sha1))
- ;
- else {
- if (nr_result)
- prepare_pack(window, depth);
- if (progress == 1 && pack_to_stdout) {
- /* the other end usually displays progress itself */
- struct itimerval v = {{0,},};
- setitimer(ITIMER_REAL, &v, NULL);
- signal(SIGALRM, SIG_IGN );
- progress_update = 0;
- }
- write_pack_file();
- if (!pack_to_stdout) {
- write_index_file();
- puts(sha1_to_hex(object_list_sha1));
- }
+ if (nr_result)
+ prepare_pack(window, depth);
+ last_obj_offset = write_pack_file();
+ if (!pack_to_stdout) {
+ unsigned char object_list_sha1[20];
+ mode_t mode = umask(0);
+
+ umask(mode);
+ mode = 0444 & ~mode;
+
+ write_index_file(last_obj_offset, object_list_sha1);
+ snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
+ base_name, sha1_to_hex(object_list_sha1));
+ if (adjust_perm(pack_tmp_name, mode))
+ die("unable to make temporary pack file readable: %s",
+ strerror(errno));
+ if (rename(pack_tmp_name, tmpname))
+ die("unable to rename temporary pack file: %s",
+ strerror(errno));
+ snprintf(tmpname, sizeof(tmpname), "%s-%s.idx",
+ base_name, sha1_to_hex(object_list_sha1));
+ if (adjust_perm(idx_tmp_name, mode))
+ die("unable to make temporary index file readable: %s",
+ strerror(errno));
+ if (rename(idx_tmp_name, tmpname))
+ die("unable to rename temporary index file: %s",
+ strerror(errno));
+ puts(sha1_to_hex(object_list_sha1));
}
if (progress)
fprintf(stderr, "Total %u (delta %u), reused %u (delta %u)\n",
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index 09774f9559b81050d89bd6663b8b672438da4342..c0329dcecdbb4775b2c438a0bf6429a5ee72b18e 100644 (file)
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
* confuse downstream git-pack-objects very badly.
*/
const char *ep = strchr(p->name, '\n');
+
+ if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1))
+ die("missing blob object '%s'", sha1_to_hex(p->item->sha1));
+
if (ep) {
printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
(int) (ep - p->name),
index 3956c5633448a5c29c60cad370ec7da6a8bfeb64..2bbda67fabfcc5292313c4ac6c462cd8911d9128 100644 (file)
--- a/builtin-unpack-objects.c
+++ b/builtin-unpack-objects.c
#include "commit.h"
#include "tag.h"
#include "tree.h"
+#include "progress.h"
static int dry_run, quiet, recover, has_errors;
static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-file";
/* We always read in 4kB chunks. */
static unsigned char buffer[4096];
-static unsigned long offset, len, consumed_bytes;
+static unsigned int offset, len;
+static off_t consumed_bytes;
static SHA_CTX ctx;
/*
die("used more bytes than were available");
len -= bytes;
offset += bytes;
+
+ /* make sure off_t is sufficiently large not to wrap */
+ if (consumed_bytes > consumed_bytes + bytes)
+ die("pack too large for current definition of off_t");
consumed_bytes += bytes;
}
struct delta_info {
unsigned char base_sha1[20];
- unsigned long base_offset;
+ unsigned nr;
+ off_t base_offset;
unsigned long size;
void *delta;
- unsigned nr;
struct delta_info *next;
};
static struct delta_info *delta_list;
static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
- unsigned long base_offset,
+ off_t base_offset,
void *delta, unsigned long size)
{
struct delta_info *info = xmalloc(sizeof(*info));
}
struct obj_info {
- unsigned long offset;
+ off_t offset;
unsigned char sha1[20];
};
} else {
unsigned base_found = 0;
unsigned char *pack, c;
- unsigned long base_offset;
+ off_t base_offset;
unsigned lo, mid, hi;
pack = fill(1);
base_offset = c & 127;
while (c & 128) {
base_offset += 1;
- if (!base_offset || base_offset & ~(~0UL >> 7))
+ if (!base_offset || MSB(base_offset, 7))
die("offset value overflow for delta base object");
pack = fill(1);
c = *pack;
free(base);
}
-static void unpack_one(unsigned nr, unsigned total)
+static void unpack_one(unsigned nr)
{
unsigned shift;
unsigned char *pack, c;
size += (c & 0x7f) << shift;
shift += 7;
}
- if (!quiet) {
- static unsigned long last_sec;
- static unsigned last_percent;
- struct timeval now;
- unsigned percentage = ((nr+1) * 100) / total;
-
- gettimeofday(&now, NULL);
- if (percentage != last_percent || now.tv_sec != last_sec) {
- last_sec = now.tv_sec;
- last_percent = percentage;
- fprintf(stderr, "%4u%% (%u/%u) done\r",
- percentage, (nr+1), total);
- }
- }
+
switch (type) {
case OBJ_COMMIT:
case OBJ_TREE:
static void unpack_all(void)
{
int i;
+ struct progress progress;
struct pack_header *hdr = fill(sizeof(struct pack_header));
unsigned nr_objects = ntohl(hdr->hdr_entries);
die("bad pack file");
if (!pack_version_ok(hdr->hdr_version))
die("unknown pack file version %d", ntohl(hdr->hdr_version));
- fprintf(stderr, "Unpacking %d objects\n", nr_objects);
+ use(sizeof(struct pack_header));
+ if (!quiet)
+ start_progress(&progress, "Unpacking %u objects...", "", nr_objects);
obj_list = xmalloc(nr_objects * sizeof(*obj_list));
- use(sizeof(struct pack_header));
- for (i = 0; i < nr_objects; i++)
- unpack_one(i, nr_objects);
+ for (i = 0; i < nr_objects; i++) {
+ unpack_one(i);
+ if (!quiet)
+ display_progress(&progress, i + 1);
+ }
+ if (!quiet)
+ stop_progress(&progress);
+
if (delta_list)
die("unresolved deltas left after unpacking");
}
}
/* All done */
- if (!quiet)
- fprintf(stderr, "\n");
return has_errors;
}
diff --git a/builtin-update-index.c b/builtin-update-index.c
index e5541df28423c4297187f5b9d42c5362fba5de29..8f9899178b1755ee0acd4a34ee185a4f54fb4219 100644 (file)
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
#include "cache-tree.h"
#include "tree-walk.h"
#include "builtin.h"
+#include "refs.h"
/*
* Default to not allowing changes to the list of files. The
return -1;
}
-static int process_file(const char *path)
+static int remove_one_path(const char *path)
{
- int size, namelen, option, status;
- struct cache_entry *ce;
- struct stat st;
-
- status = lstat(path, &st);
-
- /* We probably want to do this in remove_file_from_cache() and
- * add_cache_entry() instead...
- */
- cache_tree_invalidate_path(active_cache_tree, path);
+ if (!allow_remove)
+ return error("%s: does not exist and --remove not passed", path);
+ if (remove_file_from_cache(path))
+ return error("%s: cannot remove from the index", path);
+ return 0;
+}
- if (status < 0 || S_ISDIR(st.st_mode)) {
- /* When we used to have "path" and now we want to add
- * "path/file", we need a way to remove "path" before
- * being able to add "path/file". However,
- * "git-update-index --remove path" would not work.
- * --force-remove can be used but this is more user
- * friendly, especially since we can do the opposite
- * case just fine without --force-remove.
- */
- if (status == 0 || (errno == ENOENT || errno == ENOTDIR)) {
- if (allow_remove) {
- if (remove_file_from_cache(path))
- return error("%s: cannot remove from the index",
- path);
- else
- return 0;
- } else if (status < 0) {
- return error("%s: does not exist and --remove not passed",
- path);
- }
- }
- if (0 == status)
- return error("%s: is a directory - add files inside instead",
- path);
- else
- return error("lstat(\"%s\"): %s", path,
- strerror(errno));
- }
+/*
+ * Handle a path that couldn't be lstat'ed. It's either:
+ * - missing file (ENOENT or ENOTDIR). That's ok if we're
+ * supposed to be removing it and the removal actually
+ * succeeds.
+ * - permission error. That's never ok.
+ */
+static int process_lstat_error(const char *path, int err)
+{
+ if (err == ENOENT || err == ENOTDIR)
+ return remove_one_path(path);
+ return error("lstat(\"%s\"): %s", path, strerror(errno));
+}
- namelen = strlen(path);
- size = cache_entry_size(namelen);
- ce = xcalloc(1, size);
- memcpy(ce->name, path, namelen);
- ce->ce_flags = htons(namelen);
- fill_stat_cache_info(ce, &st);
-
- if (trust_executable_bit && has_symlinks)
- ce->ce_mode = create_ce_mode(st.st_mode);
- else {
- /* If there is an existing entry, pick the mode bits and type
- * from it, otherwise assume unexecutable regular file.
- */
- struct cache_entry *ent;
- int pos = cache_name_pos(path, namelen);
+static int add_one_path(struct cache_entry *old, const char *path, int len, struct stat *st)
+{
+ int option, size = cache_entry_size(len);
+ struct cache_entry *ce = xcalloc(1, size);
- ent = (0 <= pos) ? active_cache[pos] : NULL;
- ce->ce_mode = ce_mode_from_stat(ent, st.st_mode);
- }
+ memcpy(ce->name, path, len);
+ ce->ce_flags = htons(len);
+ fill_stat_cache_info(ce, st);
+ ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
- if (index_path(ce->sha1, path, &st, !info_only))
+ if (index_path(ce->sha1, path, st, !info_only))
return -1;
option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
if (add_cache_entry(ce, option))
- return error("%s: cannot add to the index - missing --add option?",
- path);
+ return error("%s: cannot add to the index - missing --add option?", path);
return 0;
}
+/*
+ * Handle a path that was a directory. Four cases:
+ *
+ * - it's already a gitlink in the index, and we keep it that
+ * way, and update it if we can (if we cannot find the HEAD,
+ * we're going to keep it unchanged in the index!)
+ *
+ * - it's a *file* in the index, in which case it should be
+ * removed as a file if removal is allowed, since it doesn't
+ * exist as such any more. If removal isn't allowed, it's
+ * an error.
+ *
+ * (NOTE! This is old and arguably fairly strange behaviour.
+ * We might want to make this an error unconditionally, and
+ * use "--force-remove" if you actually want to force removal).
+ *
+ * - it used to exist as a subdirectory (ie multiple files with
+ * this particular prefix) in the index, in which case it's wrong
+ * to try to update it as a directory.
+ *
+ * - it doesn't exist at all in the index, but it is a valid
+ * git directory, and it should be *added* as a gitlink.
+ */
+static int process_directory(const char *path, int len, struct stat *st)
+{
+ unsigned char sha1[20];
+ int pos = cache_name_pos(path, len);
+
+ /* Exact match: file or existing gitlink */
+ if (pos >= 0) {
+ struct cache_entry *ce = active_cache[pos];
+ if (S_ISDIRLNK(ntohl(ce->ce_mode))) {
+
+ /* Do nothing to the index if there is no HEAD! */
+ if (resolve_gitlink_ref(path, "HEAD", sha1) < 0)
+ return 0;
+
+ return add_one_path(ce, path, len, st);
+ }
+ /* Should this be an unconditional error? */
+ return remove_one_path(path);
+ }
+
+ /* Inexact match: is there perhaps a subdirectory match? */
+ pos = -pos-1;
+ while (pos < active_nr) {
+ struct cache_entry *ce = active_cache[pos++];
+
+ if (strncmp(ce->name, path, len))
+ break;
+ if (ce->name[len] > '/')
+ break;
+ if (ce->name[len] < '/')
+ continue;
+
+ /* Subdirectory match - error out */
+ return error("%s: is a directory - add individual files instead", path);
+ }
+
+ /* No match - should we add it as a gitlink? */
+ if (!resolve_gitlink_ref(path, "HEAD", sha1))
+ return add_one_path(NULL, path, len, st);
+
+ /* Error out. */
+ return error("%s: is a directory - add files inside instead", path);
+}
+
+/*
+ * Process a regular file
+ */
+static int process_file(const char *path, int len, struct stat *st)
+{
+ int pos = cache_name_pos(path, len);
+ struct cache_entry *ce = pos < 0 ? NULL : active_cache[pos];
+
+ if (ce && S_ISDIRLNK(ntohl(ce->ce_mode)))
+ return error("%s is already a gitlink, not replacing", path);
+
+ return add_one_path(ce, path, len, st);
+}
+
+static int process_path(const char *path)
+{
+ int len;
+ struct stat st;
+
+ /* We probably want to do this in remove_file_from_cache() and
+ * add_cache_entry() instead...
+ */
+ cache_tree_invalidate_path(active_cache_tree, path);
+
+ /*
+ * First things first: get the stat information, to decide
+ * what to do about the pathname!
+ */
+ if (lstat(path, &st) < 0)
+ return process_lstat_error(path, errno);
+
+ len = strlen(path);
+ if (S_ISDIR(st.st_mode))
+ return process_directory(path, len, &st);
+
+ return process_file(path, len, &st);
+}
+
static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
const char *path, int stage)
{
report("remove '%s'", path);
goto free_return;
}
- if (process_file(p))
- die("Unable to process file %s", path);
+ if (process_path(p))
+ die("Unable to process path %s", path);
report("add '%s'", path);
free_return:
if (p < path || p > path + strlen(path))
diff --git a/builtin.h b/builtin.h
index af203e9e367b1dc1abb012234b5a8ae5e09f1629..d3f3a7496e1c1adbe3f8bae36603fafee374c8c5 100644 (file)
--- a/builtin.h
+++ b/builtin.h
extern int cmd_bundle(int argc, const char **argv, const char *prefix);
extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
+extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
extern int cmd_cherry(int argc, const char **argv, const char *prefix);
extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
diff --git a/cache-tree.c b/cache-tree.c
index 9b73c8669a0946c3bcbf1de777e9acd4cd34bcae..6369cc7c536ba7b82a6afcb191628beefe889b72 100644 (file)
--- a/cache-tree.c
+++ b/cache-tree.c
mode = ntohl(ce->ce_mode);
entlen = pathlen - baselen;
}
- if (!missing_ok && !has_sha1_file(sha1))
+ if (mode != S_IFDIRLNK && !missing_ok && !has_sha1_file(sha1))
return error("invalid object %s", sha1_to_hex(sha1));
if (!ce->ce_mode)
index 53c23413e4ba0b6c43632be821c69c8eb80db857..89aaf0022d95dfe8ffda4031021e4d059998d28c 100644 (file)
--- a/cache.h
+++ b/cache.h
#define DTYPE(de) DT_UNKNOWN
#endif
+/*
+ * A "directory link" is a link to another git directory.
+ *
+ * The value 0160000 is not normally a valid mode, and
+ * also just happens to be S_IFDIR + S_IFLNK
+ *
+ * NOTE! We *really* shouldn't depend on the S_IFxxx macros
+ * always having the same values everywhere. We should use
+ * our internal git values for these things, and then we can
+ * translate that to the OS-specific value. It just so
+ * happens that everybody shares the same bit representation
+ * in the UNIX world (and apparently wider too..)
+ */
+#define S_IFDIRLNK 0160000
+#define S_ISDIRLNK(m) (((m) & S_IFMT) == S_IFDIRLNK)
+
/*
* Intensive research over the course of many years has shown that
* port 9418 is totally unused by anything else. Or
{
if (S_ISLNK(mode))
return htonl(S_IFLNK);
+ if (S_ISDIR(mode) || S_ISDIRLNK(mode))
+ return htonl(S_IFDIRLNK);
return htonl(S_IFREG | ce_permissions(mode));
}
static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode)
@@ -121,7 +139,7 @@ static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned in
}
#define canon_mode(mode) \
(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
- S_ISLNK(mode) ? S_IFLNK : S_IFDIR)
+ S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFDIRLNK)
#define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
#define CONFIG_ENVIRONMENT "GIT_CONFIG"
#define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL"
#define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
+#define GITATTRIBUTES_FILE ".gitattributes"
+#define INFOATTRIBUTES_FILE "info/attributes"
+#define ATTRIBUTE_MACRO_PREFIX "[attr]"
extern int is_bare_repository_cfg;
extern int is_bare_repository(void);
struct lock_file {
struct lock_file *next;
+ pid_t owner;
char on_list;
char filename[PATH_MAX];
};
extern struct packed_git {
struct packed_git *next;
struct pack_window *windows;
- const void *index_data;
- off_t index_size;
off_t pack_size;
- time_t mtime;
+ const void *index_data;
+ size_t index_size;
+ uint32_t num_objects;
int index_version;
+ time_t mtime;
int pack_fd;
int pack_local;
unsigned char sha1[20];
extern unsigned char* use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
extern void unuse_pack(struct pack_window **);
extern struct packed_git *add_packed_git(const char *, int, int);
-extern uint32_t num_packed_objects(const struct packed_git *p);
extern const unsigned char *nth_packed_object_sha1(const struct packed_git *, uint32_t);
extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
+extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
/* Dumb servers support */
void encode_85(char *buf, const unsigned char *data, int bytes);
/* alloc.c */
-struct blob;
-struct tree;
-struct commit;
-struct tag;
-extern struct blob *alloc_blob_node(void);
-extern struct tree *alloc_tree_node(void);
-extern struct commit *alloc_commit_node(void);
-extern struct tag *alloc_tag_node(void);
+extern void *alloc_blob_node(void);
+extern void *alloc_tree_node(void);
+extern void *alloc_commit_node(void);
+extern void *alloc_tag_node(void);
+extern void *alloc_object_node(void);
extern void alloc_report(void);
/* trace.c */
extern void trace_argv_printf(const char **argv, int count, const char *format, ...);
/* convert.c */
-extern int convert_to_git(const char *path, char **bufp, unsigned long *sizep);
-extern int convert_to_working_tree(const char *path, char **bufp, unsigned long *sizep);
+extern char *convert_to_git(const char *path, const char *src, unsigned long *sizep);
+extern char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep);
/* match-trees.c */
void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
diff --git a/combine-diff.c b/combine-diff.c
index 3a9b32f6b8882f3adb91e5833c205635657a98b7..cff9c5dc426cae9bd517614e00739edc4b40d635 100644 (file)
--- a/combine-diff.c
+++ b/combine-diff.c
diffopts = *opt;
diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
diffopts.recursive = 1;
+ diffopts.allow_external = 0;
show_log_first = !!rev->loginfo && !rev->no_commit_id;
needsep = 0;
diff --git a/commit.c b/commit.c
index 952095faa70dd8f5166f106a83382e50e131897a..10466c4ae0cb1db8648b9191fece9d9dbf238cb6 100644 (file)
--- a/commit.c
+++ b/commit.c
struct commit *lookup_commit(const unsigned char *sha1)
{
struct object *obj = lookup_object(sha1);
- if (!obj) {
- struct commit *ret = alloc_commit_node();
- created_object(sha1, &ret->object);
- ret->object.type = OBJ_COMMIT;
- return ret;
- }
+ if (!obj)
+ return create_object(sha1, OBJ_COMMIT, alloc_commit_node());
if (!obj->type)
obj->type = OBJ_COMMIT;
return check_commit(obj, sha1, 0);
index 7c03403484f3a52c9588aa3bfc58dea4f394dabe..46356e8a274a20c4d01f43c60caf98c2e7e1b76a 100755 (executable)
core.legacyHeaders
core.packedGitWindowSize
core.packedGitLimit
+ clean.requireForce
color.branch
color.branch.current
color.branch.local
index 521b2fcd32da4103d0c916af0ae44fbe53ed282a..2d80e2bad2e6f322d7ff7e9f03a6897a11f74231 100755 (executable)
--- a/contrib/gitview/gitview
+++ b/contrib/gitview/gitview
This program is based on bzrk by Scott James Remnant <scott@ubuntu.com>
"""
__copyright__ = "Copyright (C) 2006 Hewlett-Packard Development Company, L.P."
-__author__ = "Aneesh Kumar K.V <aneesh.kumar@hp.com>"
+__copyright__ = "Copyright (C) 2007 Aneesh Kumar K.V <aneesh.kumar@gmail.com"
+__author__ = "Aneesh Kumar K.V <aneesh.kumar@gmail.com>"
import sys
import cairo
import math
import string
+import fcntl
try:
import gtksourceview
fp.close()
return diff
+class AnnotateWindow:
+ """Annotate window.
+ This object represents and manages a single window containing the
+ annotate information of the file
+ """
+
+ def __init__(self):
+ self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ self.window.set_border_width(0)
+ self.window.set_title("Git repository browser annotation window")
+
+ # Use two thirds of the screen by default
+ screen = self.window.get_screen()
+ monitor = screen.get_monitor_geometry(0)
+ width = int(monitor.width * 0.66)
+ height = int(monitor.height * 0.66)
+ self.window.set_default_size(width, height)
+
+ def add_file_data(self, filename, commit_sha1, line_num):
+ fp = os.popen("git cat-file blob " + commit_sha1 +":"+filename)
+ i = 1;
+ for line in fp.readlines():
+ line = string.rstrip(line)
+ self.model.append(None, ["HEAD", filename, line, i])
+ i = i+1
+ fp.close()
+
+ # now set the cursor position
+ self.treeview.set_cursor(line_num-1)
+ self.treeview.grab_focus()
+
+ def _treeview_cursor_cb(self, *args):
+ """Callback for when the treeview cursor changes."""
+ (path, col) = self.treeview.get_cursor()
+ commit_sha1 = self.model[path][0]
+ commit_msg = ""
+ fp = os.popen("git cat-file commit " + commit_sha1)
+ for line in fp.readlines():
+ commit_msg = commit_msg + line
+ fp.close()
+
+ self.commit_buffer.set_text(commit_msg)
+
+ def _treeview_row_activated(self, *args):
+ """Callback for when the treeview row gets selected."""
+ (path, col) = self.treeview.get_cursor()
+ commit_sha1 = self.model[path][0]
+ filename = self.model[path][1]
+ line_num = self.model[path][3]
+
+ window = AnnotateWindow();
+ fp = os.popen("git rev-parse "+ commit_sha1 + "~1")
+ commit_sha1 = string.strip(fp.readline())
+ fp.close()
+ window.annotate(filename, commit_sha1, line_num)
+
+ def data_ready(self, source, condition):
+ while (1):
+ try :
+ buffer = source.read(8192)
+ except:
+ # resource temporary not available
+ return True
+
+ if (len(buffer) == 0):
+ gobject.source_remove(self.io_watch_tag)
+ source.close()
+ return False
+
+ for buff in buffer.split("\n"):
+ annotate_line = re.compile('^([0-9a-f]{40}) (.+) (.+) (.+)$')
+ m = annotate_line.match(buff)
+ if not m:
+ annotate_line = re.compile('^(filename) (.+)$')
+ m = annotate_line.match(buff)
+ if not m:
+ continue
+ filename = m.group(2)
+ else:
+ self.commit_sha1 = m.group(1)
+ self.source_line = int(m.group(2))
+ self.result_line = int(m.group(3))
+ self.count = int(m.group(4))
+ #set the details only when we have the file name
+ continue
+
+ while (self.count > 0):
+ # set at result_line + count-1 the sha1 as commit_sha1
+ self.count = self.count - 1
+ iter = self.model.iter_nth_child(None, self.result_line + self.count-1)
+ self.model.set(iter, 0, self.commit_sha1, 1, filename, 3, self.source_line)
+
+
+ def annotate(self, filename, commit_sha1, line_num):
+ # verify the commit_sha1 specified has this filename
+
+ fp = os.popen("git ls-tree "+ commit_sha1 + " -- " + filename)
+ line = string.strip(fp.readline())
+ if line == '':
+ # pop up the message the file is not there as a part of the commit
+ fp.close()
+ dialog = gtk.MessageDialog(parent=None, flags=0,
+ type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE,
+ message_format=None)
+ dialog.set_markup("The file %s is not present in the parent commit %s" % (filename, commit_sha1))
+ dialog.run()
+ dialog.destroy()
+ return
+
+ fp.close()
+
+ vpan = gtk.VPaned();
+ self.window.add(vpan);
+ vpan.show()
+
+ scrollwin = gtk.ScrolledWindow()
+ scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scrollwin.set_shadow_type(gtk.SHADOW_IN)
+ vpan.pack1(scrollwin, True, True);
+ scrollwin.show()
+
+ self.model = gtk.TreeStore(str, str, str, int)
+ self.treeview = gtk.TreeView(self.model)
+ self.treeview.set_rules_hint(True)
+ self.treeview.set_search_column(0)
+ self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
+ self.treeview.connect("row-activated", self._treeview_row_activated)
+ scrollwin.add(self.treeview)
+ self.treeview.show()
+
+ cell = gtk.CellRendererText()
+ cell.set_property("width-chars", 10)
+ cell.set_property("ellipsize", pango.ELLIPSIZE_END)
+ column = gtk.TreeViewColumn("Commit")
+ column.set_resizable(True)
+ column.pack_start(cell, expand=True)
+ column.add_attribute(cell, "text", 0)
+ self.treeview.append_column(column)
+
+ cell = gtk.CellRendererText()
+ cell.set_property("width-chars", 20)
+ cell.set_property("ellipsize", pango.ELLIPSIZE_END)
+ column = gtk.TreeViewColumn("File Name")
+ column.set_resizable(True)
+ column.pack_start(cell, expand=True)
+ column.add_attribute(cell, "text", 1)
+ self.treeview.append_column(column)
+
+ cell = gtk.CellRendererText()
+ cell.set_property("width-chars", 20)
+ cell.set_property("ellipsize", pango.ELLIPSIZE_END)
+ column = gtk.TreeViewColumn("Data")
+ column.set_resizable(True)
+ column.pack_start(cell, expand=True)
+ column.add_attribute(cell, "text", 2)
+ self.treeview.append_column(column)
+
+ # The commit message window
+ scrollwin = gtk.ScrolledWindow()
+ scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scrollwin.set_shadow_type(gtk.SHADOW_IN)
+ vpan.pack2(scrollwin, True, True);
+ scrollwin.show()
+
+ commit_text = gtk.TextView()
+ self.commit_buffer = gtk.TextBuffer()
+ commit_text.set_buffer(self.commit_buffer)
+ scrollwin.add(commit_text)
+ commit_text.show()
+
+ self.window.show()
+
+ self.add_file_data(filename, commit_sha1, line_num)
+
+ fp = os.popen("git blame --incremental -- " + filename + " " + commit_sha1)
+ flags = fcntl.fcntl(fp.fileno(), fcntl.F_GETFL)
+ fcntl.fcntl(fp.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK)
+ self.io_watch_tag = gobject.io_add_watch(fp, gobject.IO_IN, self.data_ready)
+
+
class DiffWindow:
"""Diff window.
This object represents and manages a single window containing the
height = int(monitor.height * 0.66)
self.window.set_default_size(width, height)
+
self.construct()
def construct(self):
vbox.pack_start(menu_bar, expand=False, fill=True)
menu_bar.show()
+ hpan = gtk.HPaned()
+
scrollwin = gtk.ScrolledWindow()
scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scrollwin.set_shadow_type(gtk.SHADOW_IN)
- vbox.pack_start(scrollwin, expand=True, fill=True)
+ hpan.pack1(scrollwin, True, True)
scrollwin.show()
if have_gtksourceview:
self.buffer = gtk.TextBuffer()
sourceview = gtk.TextView(self.buffer)
+
sourceview.set_editable(False)
sourceview.modify_font(pango.FontDescription("Monospace"))
scrollwin.add(sourceview)
sourceview.show()
+ # The file hierarchy: a scrollable treeview
+ scrollwin = gtk.ScrolledWindow()
+ scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scrollwin.set_shadow_type(gtk.SHADOW_IN)
+ scrollwin.set_size_request(20, -1)
+ hpan.pack2(scrollwin, True, True)
+ scrollwin.show()
+
+ self.model = gtk.TreeStore(str, str, str)
+ self.treeview = gtk.TreeView(self.model)
+ self.treeview.set_search_column(1)
+ self.treeview.connect("cursor-changed", self._treeview_clicked)
+ scrollwin.add(self.treeview)
+ self.treeview.show()
+
+ cell = gtk.CellRendererText()
+ cell.set_property("width-chars", 20)
+ column = gtk.TreeViewColumn("Select to annotate")
+ column.pack_start(cell, expand=True)
+ column.add_attribute(cell, "text", 0)
+ self.treeview.append_column(column)
+
+ vbox.pack_start(hpan, expand=True, fill=True)
+ hpan.show()
+
+ def _treeview_clicked(self, *args):
+ """Callback for when the treeview cursor changes."""
+ (path, col) = self.treeview.get_cursor()
+ specific_file = self.model[path][1]
+ commit_sha1 = self.model[path][2]
+ if specific_file == None :
+ return
+ elif specific_file == "" :
+ specific_file = None
+
+ window = AnnotateWindow();
+ window.annotate(specific_file, commit_sha1, 1)
+
+
+ def commit_files(self, commit_sha1, parent_sha1):
+ self.model.clear()
+ add = self.model.append(None, [ "Added", None, None])
+ dele = self.model.append(None, [ "Deleted", None, None])
+ mod = self.model.append(None, [ "Modified", None, None])
+ diff_tree = re.compile('^(:.{6}) (.{6}) (.{40}) (.{40}) (A|D|M)\s(.+)$')
+ fp = os.popen("git diff-tree -r --no-commit-id " + parent_sha1 + " " + commit_sha1)
+ while 1:
+ line = string.strip(fp.readline())
+ if line == '':
+ break
+ m = diff_tree.match(line)
+ if not m:
+ continue
+
+ attr = m.group(5)
+ filename = m.group(6)
+ if attr == "A":
+ self.model.append(add, [filename, filename, commit_sha1])
+ elif attr == "D":
+ self.model.append(dele, [filename, filename, commit_sha1])
+ elif attr == "M":
+ self.model.append(mod, [filename, filename, commit_sha1])
+ fp.close()
+
+ self.treeview.expand_all()
def set_diff(self, commit_sha1, parent_sha1, encoding):
"""Set the differences showed by this window.
fp = os.popen("git diff-tree -p " + parent_sha1 + " " + commit_sha1)
self.buffer.set_text(unicode(fp.read(), encoding).encode('utf-8'))
fp.close()
+ self.commit_files(commit_sha1, parent_sha1)
self.window.show()
def save_menu_response(self, widget, string):
class GitView:
""" This is the main class
"""
- version = "0.8"
+ version = "0.9"
def __init__(self, with_diff=0):
self.with_diff = with_diff
dialog = gtk.AboutDialog()
dialog.set_name("Gitview")
dialog.set_version(GitView.version)
- dialog.set_authors(["Aneesh Kumar K.V <aneesh.kumar@hp.com>"])
+ dialog.set_authors(["Aneesh Kumar K.V <aneesh.kumar@gmail.com>"])
dialog.set_website("http://www.kernel.org/pub/software/scm/git/")
dialog.set_copyright("Use and distribute under the terms of the GNU General Public License")
dialog.set_wrap_license(True)
diff --git a/convert.c b/convert.c
index 898bfe3eb219618e746afbe02eb4a8756eccc6aa..ad106ef35fb0729709b243d6292980f15cd1b0a5 100644 (file)
--- a/convert.c
+++ b/convert.c
#include "cache.h"
+#include "attr.h"
+
/*
* convert.c - convert a file when checking it out and checking it in.
*
* translation when the "auto_crlf" option is set.
*/
+#define CRLF_GUESS (-1)
+#define CRLF_BINARY 0
+#define CRLF_TEXT 1
+#define CRLF_INPUT 2
+
struct text_stat {
/* CR, LF and CRLF counts */
unsigned cr, lf, crlf;
return 0;
}
-int convert_to_git(const char *path, char **bufp, unsigned long *sizep)
+static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep, int action)
{
- char *buffer, *nbuf;
+ char *buffer, *dst;
unsigned long size, nsize;
struct text_stat stats;
- /*
- * FIXME! Other pluggable conversions should go here,
- * based on filename patterns. Right now we just do the
- * stupid auto-CRLF one.
- */
- if (!auto_crlf)
- return 0;
+ if ((action == CRLF_BINARY) || (action == CRLF_GUESS && !auto_crlf))
+ return NULL;
size = *sizep;
if (!size)
- return 0;
- buffer = *bufp;
+ return NULL;
- gather_stats(buffer, size, &stats);
+ gather_stats(src, size, &stats);
/* No CR? Nothing to convert, regardless. */
if (!stats.cr)
- return 0;
-
- /*
- * We're currently not going to even try to convert stuff
- * that has bare CR characters. Does anybody do that crazy
- * stuff?
- */
- if (stats.cr != stats.crlf)
- return 0;
-
- /*
- * And add some heuristics for binary vs text, of course...
- */
- if (is_binary(size, &stats))
- return 0;
+ return NULL;
+
+ if (action == CRLF_GUESS) {
+ /*
+ * We're currently not going to even try to convert stuff
+ * that has bare CR characters. Does anybody do that crazy
+ * stuff?
+ */
+ if (stats.cr != stats.crlf)
+ return NULL;
+
+ /*
+ * And add some heuristics for binary vs text, of course...
+ */
+ if (is_binary(size, &stats))
+ return NULL;
+ }
/*
- * Ok, allocate a new buffer, fill it in, and return true
- * to let the caller know that we switched buffers on it.
+ * Ok, allocate a new buffer, fill it in, and return it
+ * to let the caller know that we switched buffers.
*/
nsize = size - stats.crlf;
- nbuf = xmalloc(nsize);
- *bufp = nbuf;
+ buffer = xmalloc(nsize);
*sizep = nsize;
- do {
- unsigned char c = *buffer++;
- if (c != '\r')
- *nbuf++ = c;
- } while (--size);
- return 1;
+ dst = buffer;
+ if (action == CRLF_GUESS) {
+ /*
+ * If we guessed, we already know we rejected a file with
+ * lone CR, and we can strip a CR without looking at what
+ * follow it.
+ */
+ do {
+ unsigned char c = *src++;
+ if (c != '\r')
+ *dst++ = c;
+ } while (--size);
+ } else {
+ do {
+ unsigned char c = *src++;
+ if (! (c == '\r' && (1 < size && *src == '\n')))
+ *dst++ = c;
+ } while (--size);
+ }
+
+ return buffer;
}
-int convert_to_working_tree(const char *path, char **bufp, unsigned long *sizep)
+static char *crlf_to_worktree(const char *path, const char *src, unsigned long *sizep, int action)
{
- char *buffer, *nbuf;
+ char *buffer, *dst;
unsigned long size, nsize;
struct text_stat stats;
unsigned char last;
- /*
- * FIXME! Other pluggable conversions should go here,
- * based on filename patterns. Right now we just do the
- * stupid auto-CRLF one.
- */
- if (auto_crlf <= 0)
- return 0;
+ if ((action == CRLF_BINARY) || (action == CRLF_INPUT) ||
+ (action == CRLF_GUESS && auto_crlf <= 0))
+ return NULL;
size = *sizep;
if (!size)
- return 0;
- buffer = *bufp;
+ return NULL;
- gather_stats(buffer, size, &stats);
+ gather_stats(src, size, &stats);
/* No LF? Nothing to convert, regardless. */
if (!stats.lf)
- return 0;
+ return NULL;
/* Was it already in CRLF format? */
if (stats.lf == stats.crlf)
- return 0;
+ return NULL;
- /* If we have any bare CR characters, we're not going to touch it */
- if (stats.cr != stats.crlf)
- return 0;
+ if (action == CRLF_GUESS) {
+ /* If we have any bare CR characters, we're not going to touch it */
+ if (stats.cr != stats.crlf)
+ return NULL;
- if (is_binary(size, &stats))
- return 0;
+ if (is_binary(size, &stats))
+ return NULL;
+ }
/*
- * Ok, allocate a new buffer, fill it in, and return true
- * to let the caller know that we switched buffers on it.
+ * Ok, allocate a new buffer, fill it in, and return it
+ * to let the caller know that we switched buffers.
*/
nsize = size + stats.lf - stats.crlf;
- nbuf = xmalloc(nsize);
- *bufp = nbuf;
+ buffer = xmalloc(nsize);
*sizep = nsize;
last = 0;
+
+ dst = buffer;
do {
- unsigned char c = *buffer++;
+ unsigned char c = *src++;
if (c == '\n' && last != '\r')
- *nbuf++ = '\r';
- *nbuf++ = c;
+ *dst++ = '\r';
+ *dst++ = c;
last = c;
} while (--size);
- return 1;
+ return buffer;
+}
+
+static void setup_convert_check(struct git_attr_check *check)
+{
+ static struct git_attr *attr_crlf;
+
+ if (!attr_crlf)
+ attr_crlf = git_attr("crlf", 4);
+ check->attr = attr_crlf;
+}
+
+static int git_path_check_crlf(const char *path, struct git_attr_check *check)
+{
+ const char *value = check->value;
+
+ if (ATTR_TRUE(value))
+ return CRLF_TEXT;
+ else if (ATTR_FALSE(value))
+ return CRLF_BINARY;
+ else if (ATTR_UNSET(value))
+ ;
+ else if (!strcmp(value, "input"))
+ return CRLF_INPUT;
+ return CRLF_GUESS;
+}
+
+char *convert_to_git(const char *path, const char *src, unsigned long *sizep)
+{
+ struct git_attr_check check[1];
+ int crlf = CRLF_GUESS;
+
+ setup_convert_check(check);
+ if (!git_checkattr(path, 1, check)) {
+ crlf = git_path_check_crlf(path, check);
+ }
+ return crlf_to_git(path, src, sizep, crlf);
+}
+
+char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep)
+{
+ struct git_attr_check check[1];
+ int crlf = CRLF_GUESS;
+
+ setup_convert_check(check);
+ if (!git_checkattr(path, 1, check)) {
+ crlf = git_path_check_crlf(path, check);
+ }
+ return crlf_to_worktree(path, src, sizep, crlf);
}
diff --git a/csum-file.c b/csum-file.c
index b7174c6c056c5a8f2a800ecbcb3cdf304c0bfc3f..7c806ada48d0fd58c091f9415fc8bb2f61bdd2e6 100644 (file)
--- a/csum-file.c
+++ b/csum-file.c
int sha1write(struct sha1file *f, void *buf, unsigned int count)
{
+ if (f->do_crc)
+ f->crc32 = crc32(f->crc32, buf, count);
while (count) {
unsigned offset = f->offset;
unsigned left = sizeof(f->buffer) - offset;
f->fd = fd;
f->error = 0;
f->offset = 0;
+ f->do_crc = 0;
SHA1_Init(&f->ctx);
return f;
}
f->fd = fd;
f->error = 0;
f->offset = 0;
+ f->do_crc = 0;
SHA1_Init(&f->ctx);
return f;
}
return size;
}
+void crc32_begin(struct sha1file *f)
+{
+ f->crc32 = crc32(0, Z_NULL, 0);
+ f->do_crc = 1;
+}
+uint32_t crc32_end(struct sha1file *f)
+{
+ f->do_crc = 0;
+ return f->crc32;
+}
diff --git a/csum-file.h b/csum-file.h
index 3ad1a992a758fc9339baa2cecc17ac14af359f1b..7e1339189dcdc6e271fad5df7ad427954642ae9e 100644 (file)
--- a/csum-file.h
+++ b/csum-file.h
unsigned int offset, namelen;
SHA_CTX ctx;
char name[PATH_MAX];
+ int do_crc;
+ uint32_t crc32;
unsigned char buffer[8192];
};
extern int sha1close(struct sha1file *, unsigned char *, int);
extern int sha1write(struct sha1file *, void *, unsigned int);
extern int sha1write_compressed(struct sha1file *, void *, unsigned int);
+extern void crc32_begin(struct sha1file *);
+extern uint32_t crc32_end(struct sha1file *);
#endif
diff --git a/decorate.c b/decorate.c
index 396b41311a0d1549897d4b92bccd12001d13bc01..23f6b0040f1cda9a550e5b1d90589fa4a7f76eb5 100644 (file)
--- a/decorate.c
+++ b/decorate.c
hash[j].decoration = decoration;
return old;
}
- j++;
if (++j >= size)
j = 0;
}
diff --git a/diff-lib.c b/diff-lib.c
index 7531e20c784c44c0b5d3ecb2057638874a09ce6c..07f4e8106a51384d2236b182438472884c300da6 100644 (file)
--- a/diff-lib.c
+++ b/diff-lib.c
continue;
}
else
- dpath->mode = canon_mode(st.st_mode);
+ dpath->mode = ntohl(ce_mode_from_stat(ce, st.st_mode));
while (i < entries) {
struct cache_entry *nce = active_cache[i];
int mode = ntohl(nce->ce_mode);
num_compare_stages++;
hashcpy(dpath->parent[stage-2].sha1, nce->sha1);
- dpath->parent[stage-2].mode =
- canon_mode(mode);
+ dpath->parent[stage-2].mode = ntohl(ce_mode_from_stat(nce, mode));
dpath->parent[stage-2].status =
DIFF_STATUS_MODIFIED;
}
if (!changed && !revs->diffopt.find_copies_harder)
continue;
oldmode = ntohl(ce->ce_mode);
-
- newmode = canon_mode(st.st_mode);
- if (!trust_executable_bit &&
- S_ISREG(newmode) && S_ISREG(oldmode) &&
- ((newmode ^ oldmode) == 0111))
- newmode = oldmode;
- else if (!has_symlinks &&
- S_ISREG(newmode) && S_ISLNK(oldmode))
- newmode = oldmode;
+ newmode = ntohl(ce_mode_from_stat(ce, st.st_mode));
diff_change(&revs->diffopt, oldmode, newmode,
ce->sha1, (changed ? null_sha1 : ce->sha1),
ce->name, NULL);
index fbb79d70a93dd0c6c46a1d24505e55f737d44189..9dfded76642c1136701fb05557c182dc92448394 100644 (file)
--- a/diff.c
+++ b/diff.c
#include "delta.h"
#include "xdiff-interface.h"
#include "color.h"
+#include "attr.h"
#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
die("bad config variable '%s'", var);
}
+static struct ll_diff_driver {
+ const char *name;
+ struct ll_diff_driver *next;
+ char *cmd;
+} *user_diff, **user_diff_tail;
+
+/*
+ * Currently there is only "diff.<drivername>.command" variable;
+ * because there are "diff.color.<slot>" variables, we are parsing
+ * this in a bit convoluted way to allow low level diff driver
+ * called "color".
+ */
+static int parse_lldiff_command(const char *var, const char *ep, const char *value)
+{
+ const char *name;
+ int namelen;
+ struct ll_diff_driver *drv;
+
+ name = var + 5;
+ namelen = ep - name;
+ for (drv = user_diff; drv; drv = drv->next)
+ if (!strncmp(drv->name, name, namelen) && !drv->name[namelen])
+ break;
+ if (!drv) {
+ char *namebuf;
+ drv = xcalloc(1, sizeof(struct ll_diff_driver));
+ namebuf = xmalloc(namelen + 1);
+ memcpy(namebuf, name, namelen);
+ namebuf[namelen] = 0;
+ drv->name = namebuf;
+ drv->next = NULL;
+ if (!user_diff_tail)
+ user_diff_tail = &user_diff;
+ *user_diff_tail = drv;
+ user_diff_tail = &(drv->next);
+ }
+
+ if (!value)
+ return error("%s: lacks value", var);
+ drv->cmd = strdup(value);
+ return 0;
+}
+
/*
* These are to give UI layer defaults.
* The core-level commands such as git-diff-files should
diff_detect_rename_default = DIFF_DETECT_RENAME;
return 0;
}
+ if (!prefixcmp(var, "diff.")) {
+ const char *ep = strrchr(var, '.');
+
+ if (ep != var + 4 && !strcmp(ep, ".command"))
+ return parse_lldiff_command(var, ep, value);
+ }
if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
int slot = parse_diff_color_slot(var, 11);
color_parse(value, var, diff_colors[slot]);
return 0;
}
+
return git_default_config(var, value);
}
emit_binary_diff_body(two, one);
}
+static void setup_diff_attr_check(struct git_attr_check *check)
+{
+ static struct git_attr *attr_diff;
+
+ if (!attr_diff)
+ attr_diff = git_attr("diff", 4);
+ check->attr = attr_diff;
+}
+
#define FIRST_FEW_BYTES 8000
-static int mmfile_is_binary(mmfile_t *mf)
+static int file_is_binary(struct diff_filespec *one)
{
- long sz = mf->size;
+ unsigned long sz;
+ struct git_attr_check attr_diff_check;
+
+ setup_diff_attr_check(&attr_diff_check);
+ if (!git_checkattr(one->path, 1, &attr_diff_check)) {
+ const char *value = attr_diff_check.value;
+ if (ATTR_TRUE(value))
+ return 0;
+ else if (ATTR_FALSE(value))
+ return 1;
+ }
+
+ if (!one->data) {
+ if (!DIFF_FILE_VALID(one))
+ return 0;
+ diff_populate_filespec(one, 0);
+ }
+ sz = one->size;
if (FIRST_FEW_BYTES < sz)
sz = FIRST_FEW_BYTES;
- return !!memchr(mf->ptr, 0, sz);
+ return !!memchr(one->data, 0, sz);
}
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 && (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))) {
+ if (!o->text && (file_is_binary(one) || file_is_binary(two))) {
/* Quite common confusing case */
if (mf1.size == mf2.size &&
!memcmp(mf1.ptr, mf2.ptr, mf1.size))
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
- if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) {
+ if (file_is_binary(one) || file_is_binary(two)) {
data->is_binary = 1;
data->added = mf2.size;
data->deleted = mf1.size;
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
- if (mmfile_is_binary(&mf2))
+ if (file_is_binary(two))
return;
else {
/* Crazy xdl interfaces.. */
return 0;
}
+static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
+{
+ int len;
+ char *data = xmalloc(100);
+ len = snprintf(data, 100,
+ "Subproject commit %s\n", sha1_to_hex(s->sha1));
+ s->data = data;
+ s->size = len;
+ s->should_free = 1;
+ if (size_only) {
+ s->data = NULL;
+ free(data);
+ }
+ return 0;
+}
+
/*
* While doing rename detection and pickaxe operation, we may need to
* grab the data for the blob (or file) for our own in-core comparison.
if (s->data)
return err;
+
+ if (S_ISDIRLNK(s->mode))
+ return diff_populate_gitlink(s, size_only);
+
if (!s->sha1_valid ||
reuse_worktree_file(s->path, s->sha1, 0)) {
struct stat st;
/*
* Convert from working tree format to canonical git format
*/
- buf = s->data;
size = s->size;
- if (convert_to_git(s->path, &buf, &size)) {
+ buf = convert_to_git(s->path, s->data, &size);
+ if (buf) {
munmap(s->data, s->size);
s->should_munmap = 0;
s->data = buf;
}
}
+static const char *external_diff_attr(const char *name)
+{
+ struct git_attr_check attr_diff_check;
+
+ setup_diff_attr_check(&attr_diff_check);
+ if (!git_checkattr(name, 1, &attr_diff_check)) {
+ const char *value = attr_diff_check.value;
+ if (!ATTR_TRUE(value) &&
+ !ATTR_FALSE(value) &&
+ !ATTR_UNSET(value)) {
+ struct ll_diff_driver *drv;
+
+ if (!user_diff_tail) {
+ user_diff_tail = &user_diff;
+ git_config(git_diff_ui_config);
+ }
+ for (drv = user_diff; drv; drv = drv->next)
+ if (!strcmp(drv->name, value))
+ return drv->cmd;
+ }
+ }
+ return NULL;
+}
+
static void run_diff_cmd(const char *pgm,
const char *name,
const char *other,
struct diff_options *o,
int complete_rewrite)
{
+ if (!o->allow_external)
+ pgm = NULL;
+ else {
+ const char *cmd = external_diff_attr(name);
+ if (cmd)
+ pgm = cmd;
+ }
+
if (pgm) {
run_external_diff(pgm, name, other, one, two, xfrm_msg,
complete_rewrite);
if (o->binary) {
mmfile_t mf;
- if ((!fill_mmfile(&mf, one) && mmfile_is_binary(&mf)) ||
- (!fill_mmfile(&mf, two) && mmfile_is_binary(&mf)))
+ if ((!fill_mmfile(&mf, one) && file_is_binary(one)) ||
+ (!fill_mmfile(&mf, two) && file_is_binary(two)))
abbrev = 40;
}
len += snprintf(msg + len, sizeof(msg) - len,
@@ -2701,7 +2830,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 (mmfile_is_binary(&mf2))
+ if (file_is_binary(p->two))
continue;
len1 = remove_space(p->one->path, strlen(p->one->path));
index a0d2ce13994c1a8751bf7b207671e95c5bc5db97..63738c1dd4c71cb1beacaffea40bf51377a137ea 100644 (file)
--- a/diff.h
+++ b/diff.h
color_diff_words:1,
has_changes:1,
quiet:1,
+ allow_external:1,
exit_with_status:1;
int context;
int break_opt;
index 7426fde330a200e3137e722c4b9adbc5ce6bdd90..d3063520b03c9c5f608cbd9e97f6b414adb4c5af 100644 (file)
--- a/dir.c
+++ b/dir.c
*/
#include "cache.h"
#include "dir.h"
+#include "refs.h"
struct path_simplify {
int len;
const char *path;
};
+static int read_directory_recursive(struct dir_struct *dir,
+ const char *path, const char *base, int baselen,
+ int check_only, const struct path_simplify *simplify);
+
int common_prefix(const char **pathspec)
{
const char *path, *slash, *next;
prefix = slash - path + 1;
while ((next = *++pathspec) != NULL) {
int len = strlen(next);
- if (len >= prefix && !memcmp(path, next, len))
+ if (len >= prefix && !memcmp(path, next, prefix))
continue;
+ len = prefix - 1;
for (;;) {
if (!len)
return 0;
@@ -286,15 +292,111 @@ struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int
return ent;
}
-static int dir_exists(const char *dirname, int len)
+enum exist_status {
+ index_nonexistent = 0,
+ index_directory,
+ index_gitdir,
+};
+
+/*
+ * The index sorts alphabetically by entry name, which
+ * means that a gitlink sorts as '\0' at the end, while
+ * a directory (which is defined not as an entry, but as
+ * the files it contains) will sort with the '/' at the
+ * end.
+ */
+static enum exist_status directory_exists_in_index(const char *dirname, int len)
{
int pos = cache_name_pos(dirname, len);
- if (pos >= 0)
- return 1;
- pos = -pos-1;
- if (pos >= active_nr) /* can't */
- return 0;
- return !strncmp(active_cache[pos]->name, dirname, len);
+ if (pos < 0)
+ pos = -pos-1;
+ while (pos < active_nr) {
+ struct cache_entry *ce = active_cache[pos++];
+ unsigned char endchar;
+
+ if (strncmp(ce->name, dirname, len))
+ break;
+ endchar = ce->name[len];
+ if (endchar > '/')
+ break;
+ if (endchar == '/')
+ return index_directory;
+ if (!endchar && S_ISDIRLNK(ntohl(ce->ce_mode)))
+ return index_gitdir;
+ }
+ return index_nonexistent;
+}
+
+/*
+ * When we find a directory when traversing the filesystem, we
+ * have three distinct cases:
+ *
+ * - ignore it
+ * - see it as a directory
+ * - recurse into it
+ *
+ * and which one we choose depends on a combination of existing
+ * git index contents and the flags passed into the directory
+ * traversal routine.
+ *
+ * Case 1: If we *already* have entries in the index under that
+ * directory name, we always recurse into the directory to see
+ * all the files.
+ *
+ * Case 2: If we *already* have that directory name as a gitlink,
+ * we always continue to see it as a gitlink, regardless of whether
+ * there is an actual git directory there or not (it might not
+ * be checked out as a subproject!)
+ *
+ * Case 3: if we didn't have it in the index previously, we
+ * have a few sub-cases:
+ *
+ * (a) if "show_other_directories" is true, we show it as
+ * just a directory, unless "hide_empty_directories" is
+ * also true and the directory is empty, in which case
+ * we just ignore it entirely.
+ * (b) if it looks like a git directory, and we don't have
+ * 'no_dirlinks' set we treat it as a gitlink, and show it
+ * as a directory.
+ * (c) otherwise, we recurse into it.
+ */
+enum directory_treatment {
+ show_directory,
+ ignore_directory,
+ recurse_into_directory,
+};
+
+static enum directory_treatment treat_directory(struct dir_struct *dir,
+ const char *dirname, int len,
+ const struct path_simplify *simplify)
+{
+ /* The "len-1" is to strip the final '/' */
+ switch (directory_exists_in_index(dirname, len-1)) {
+ case index_directory:
+ return recurse_into_directory;
+
+ case index_gitdir:
+ if (dir->show_other_directories)
+ return ignore_directory;
+ return show_directory;
+
+ case index_nonexistent:
+ if (dir->show_other_directories)
+ break;
+ if (!dir->no_dirlinks) {
+ unsigned char sha1[20];
+ if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0)
+ return show_directory;
+ }
+ return recurse_into_directory;
+ }
+
+ /* This is the "show_other_directories" case */
+ if (!dir->hide_empty_directories)
+ return show_directory;
+ if (!read_directory_recursive(dir, dirname, dirname, len, 1, simplify))
+ return ignore_directory;
+ return show_directory;
}
/*
@@ -353,6 +455,9 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
!strcmp(de->d_name + 1, "git")))
continue;
len = strlen(de->d_name);
+ /* Ignore overly long pathnames! */
+ if (len + baselen + 8 > sizeof(fullname))
+ continue;
memcpy(fullname + baselen, de->d_name, len+1);
if (simplify_away(fullname, baselen + len, simplify))
continue;
@@ -377,19 +482,17 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
case DT_DIR:
memcpy(fullname + baselen + len, "/", 2);
len++;
- if (dir->show_other_directories &&
- !dir_exists(fullname, baselen + len)) {
- if (dir->hide_empty_directories &&
- !read_directory_recursive(dir,
- fullname, fullname,
- baselen + len, 1, simplify))
- continue;
+ switch (treat_directory(dir, fullname, baselen + len, simplify)) {
+ case show_directory:
break;
+ case recurse_into_directory:
+ contents += read_directory_recursive(dir,
+ fullname, fullname, baselen + len, 0, simplify);
+ continue;
+ case ignore_directory:
+ continue;
}
-
- contents += read_directory_recursive(dir,
- fullname, fullname, baselen + len, 0, simplify);
- continue;
+ break;
case DT_REG:
case DT_LNK:
break;
index 33c31f25fbabc36db26e6fdf9f33381f166d2d7f..817c674da1e017cffea9dddae672f9125aca8475 100644 (file)
--- a/dir.h
+++ b/dir.h
int nr, alloc;
unsigned int show_ignored:1,
show_other_directories:1,
- hide_empty_directories:1;
+ hide_empty_directories:1,
+ no_dirlinks:1;
struct dir_entry **entries;
/* Exclude info */
index d72f811580ad10e792e38b40fe79bf4af3868846..84f78025ff9f24d3ef9b2412894e18f3d9fc1859 100644 (file)
--- a/entry.c
+++ b/entry.c
return open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
}
+static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned long *size)
+{
+ enum object_type type;
+ void *new = read_sha1_file(ce->sha1, &type, size);
+
+ if (new) {
+ if (type == OBJ_BLOB)
+ return new;
+ free(new);
+ }
+ return NULL;
+}
+
static int write_entry(struct cache_entry *ce, char *path, struct checkout *state, int to_tempfile)
{
int fd;
- void *new;
- unsigned long size;
long wrote;
- enum object_type type;
- new = read_sha1_file(ce->sha1, &type, &size);
- if (!new || type != OBJ_BLOB) {
- if (new)
- free(new);
- return error("git-checkout-index: unable to read sha1 file of %s (%s)",
- path, sha1_to_hex(ce->sha1));
- }
switch (ntohl(ce->ce_mode) & S_IFMT) {
- char *buf;
- unsigned long nsize;
+ char *buf, *new;
+ unsigned long size;
case S_IFREG:
+ new = read_blob_entry(ce, path, &size);
+ if (!new)
+ return error("git-checkout-index: unable to read sha1 file of %s (%s)",
+ path, sha1_to_hex(ce->sha1));
if (to_tempfile) {
strcpy(path, ".merge_file_XXXXXX");
fd = mkstemp(path);
@@ -96,12 +103,10 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
/*
* Convert from git internal format to working tree format
*/
- buf = new;
- nsize = size;
- if (convert_to_working_tree(ce->name, &buf, &nsize)) {
+ buf = convert_to_working_tree(ce->name, new, &size);
+ if (buf) {
free(new);
new = buf;
- size = nsize;
}
wrote = write_in_full(fd, new, size);
@@ -111,6 +116,10 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
return error("git-checkout-index: unable to write file %s", path);
break;
case S_IFLNK:
+ new = read_blob_entry(ce, path, &size);
+ if (!new)
+ return error("git-checkout-index: unable to read sha1 file of %s (%s)",
+ path, sha1_to_hex(ce->sha1));
if (to_tempfile || !has_symlinks) {
if (to_tempfile) {
strcpy(path, ".merge_link_XXXXXX");
@@ -136,8 +145,13 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
"symlink %s (%s)", path, strerror(errno));
}
break;
+ case S_IFDIRLNK:
+ if (to_tempfile)
+ return error("git-checkout-index: cannot create temporary subproject %s", path);
+ if (mkdir(path, 0777) < 0)
+ return error("git-checkout-index: cannot create subproject directory %s", path);
+ break;
default:
- free(new);
return error("git-checkout-index: unknown file mode for %s", path);
}
@@ -179,6 +193,9 @@ int checkout_entry(struct cache_entry *ce, struct checkout *state, char *topath)
*/
unlink(path);
if (S_ISDIR(st.st_mode)) {
+ /* If it is a gitlink, leave it alone! */
+ if (S_ISDIRLNK(ntohl(ce->ce_mode)))
+ return 0;
if (!state->force)
return error("%s is a directory", path);
remove_subtree(path);
diff --git a/git-am.sh b/git-am.sh
index e69ecbfdb1a817b477aff8618f284a4c921e00e5..c9f66e278454374714794477be7ff8b93cd05a0f 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
<"$dotest/$msgnum" >"$dotest/info" ||
stop_here $this
test -s $dotest/patch || {
- echo "Patch is empty. Was is split wrong?"
+ echo "Patch is empty. Was it split wrong?"
stop_here $this
}
git-stripspace < "$dotest/msg" > "$dotest/msg-clean"
diff --git a/git-checkout.sh b/git-checkout.sh
index deb0a9a3c733ed889158d05b7cae4d174917553d..ed7c2c5f6aab338c844329e3ae3d9c7ce003680f 100755 (executable)
--- a/git-checkout.sh
+++ b/git-checkout.sh
newbranch_log=
merge=
quiet=
+v=-v
LF='
'
while [ "$#" != "0" ]; do
;;
"-q")
quiet=1
+ v=
;;
--)
break
if [ "$force" ]
then
- git-read-tree --reset -u $new
+ git-read-tree $v --reset -u $new
else
git-update-index --refresh >/dev/null
merge_error=$(git-read-tree -m -u --exclude-per-directory=.gitignore $old $new 2>&1) || (
# Match the index to the working tree, and do a three-way.
git diff-files --name-only | git update-index --remove --stdin &&
work=`git write-tree` &&
- git read-tree --reset -u $new || exit
+ git read-tree $v --reset -u $new || exit
eval GITHEAD_$new='${new_name:-${branch:-$new}}' &&
eval GITHEAD_$work=local &&
# this is not a real merge before committing, but just carrying
# the working tree changes along.
unmerged=`git ls-files -u`
- git read-tree --reset $new
+ git read-tree $v --reset $new
case "$unmerged" in
'') ;;
*)
diff --git a/git-clean.sh b/git-clean.sh
index db177a7886b6407b4c4ad7b778a1ae99471355ac..299309d97169e6d9a52a8175dd0b8f6d54c5b369 100755 (executable)
--- a/git-clean.sh
+++ b/git-clean.sh
# Copyright (c) 2005-2006 Pavel Roskin
#
-USAGE="[-d] [-n] [-q] [-x | -X] [--] <paths>..."
+USAGE="[-d] [-f] [-n] [-q] [-x | -X] [--] <paths>..."
LONG_USAGE='Clean untracked files from the working directory
-d remove directories as well
+ -f override clean.requireForce and clean anyway
-n don'\''t remove anything, just show what would be done
-q be quiet, only report errors
-x remove ignored files as well
ignored=
ignoredonly=
cleandir=
+disabled="`git-config --bool clean.requireForce`"
rmf="rm -f --"
rmrf="rm -rf --"
rm_refuse="echo Not removing"
-d)
cleandir=1
;;
+ -f)
+ disabled=
+ ;;
-n)
+ disabled=
rmf="echo Would remove"
rmrf="echo Would remove"
rm_refuse="echo Would not remove"
shift
done
+if [ "$disabled" = true ]; then
+ echo "clean.requireForce set and -n or -f not given; refusing to clean"
+ exit 1
+fi
+
case "$ignored,$ignoredonly" in
1,1) usage;;
esac
diff --git a/git-clone.sh b/git-clone.sh
index 513b574d13858f1a81e6f66251890d81bf0e55ce..cad5c0c088fa997c6f8f36c44e8888a5cb19515e 100755 (executable)
--- a/git-clone.sh
+++ b/git-clone.sh
else
tname=$name
fi
- git-http-fetch $v -a -w "$tname" "$name" "$1" || exit 1
+ git-http-fetch $v -a -w "$tname" "$sha1" "$1" || exit 1
done <"$clone_tmp/refs"
rm -fr "$clone_tmp"
http_fetch "$1/HEAD" "$GIT_DIR/REMOTE_HEAD" ||
diff --git a/git-compat-util.h b/git-compat-util.h
index 5f6a281b78245bbb4e1e480d81cfb8c1b4cfa6ac..0b6d74d4d7ca0df726dd0464951a5e0f045c8715 100644 (file)
--- a/git-compat-util.h
+++ b/git-compat-util.h
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#ifdef __GNUC__
+#define TYPEOF(x) (__typeof__(x))
+#else
+#define TYPEOF(x)
+#endif
+
+#define MSB(x, bits) ((x) & TYPEOF(x)(~0ULL << (sizeof(x) * 8 - (bits))))
+
#if !defined(__APPLE__) && !defined(__FreeBSD__)
#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
diff --git a/git-fetch.sh b/git-fetch.sh
index b04bd553f86213478a36f8ec2f19476f02ccf09f..0e05cf1195737d2c7afc4b9447d7b4105908bf77 100755 (executable)
--- a/git-fetch.sh
+++ b/git-fetch.sh
git-bundle unbundle "$remote" $rref ||
echo failed "$remote"
else
- git-fetch-pack --thin $exec $keep $shallow_depth \
- $quiet $no_progress "$remote" $rref ||
- echo failed "$remote"
+ if test -d "$remote" &&
+
+ # The remote might be our alternate. With
+ # this optimization we will bypass fetch-pack
+ # altogether, which means we cannot be doing
+ # the shallow stuff at all.
+ test ! -f "$GIT_DIR/shallow" &&
+ test -z "$shallow_depth" &&
+
+ # See if all of what we are going to fetch are
+ # connected to our repository's tips, in which
+ # case we do not have to do any fetch.
+ theirs=$(echo "$ls_remote_result" | \
+ git-fetch--tool -s pick-rref "$rref" "-") &&
+
+ # This will barf when $theirs reach an object that
+ # we do not have in our repository. Otherwise,
+ # we already have everything the fetch would bring in.
+ git-rev-list --objects $theirs --not --all \
+ >/dev/null 2>/dev/null
+ then
+ echo "$ls_remote_result" | \
+ git-fetch--tool pick-rref "$rref" "-"
+ else
+ git-fetch-pack --thin $exec $keep $shallow_depth \
+ $quiet $no_progress "$remote" $rref ||
+ echo failed "$remote"
+ fi
fi
) |
(
fi
# Find $remote_name from ls-remote output.
- head=$(
- IFS=' '
- echo "$ls_remote_result" |
- while read sha1 name
- do
- test "z$name" = "z$remote_name" || continue
- echo "$sha1"
- break
- done
- )
+ head=$(echo "$ls_remote_result" | \
+ git-fetch--tool -s pick-rref "$remote_name" "-")
expr "z$head" : "z$_x40\$" >/dev/null ||
die "No such ref $remote_name at $remote"
echo >&2 "Fetching $remote_name from $remote using $proto"
diff --git a/git-reset.sh b/git-reset.sh
index fee6d98d9cba708828c36a2d2a29dbec8662c36b..a172d7ce25b40f192b0036d29a8a60e62649159c 100755 (executable)
--- a/git-reset.sh
+++ b/git-reset.sh
die "Cannot do a soft reset in the middle of a merge."
fi
else
- git-read-tree --reset $update "$rev" || exit
+ git-read-tree -v --reset $update "$rev" || exit
fi
# Any resets update HEAD to the head being switched to.
index 7def319e609454cd2389d2fc60caeaa5831d339e..f20090721aa799a8dfd780291c8a83bd834b45b5 100644 (file)
--- a/git.c
+++ b/git.c
{ "cat-file", cmd_cat_file, RUN_SETUP },
{ "checkout-index", cmd_checkout_index, RUN_SETUP },
{ "check-ref-format", cmd_check_ref_format },
+ { "check-attr", cmd_check_attr, RUN_SETUP | NOT_BARE },
{ "cherry", cmd_cherry, RUN_SETUP },
{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NOT_BARE },
{ "commit-tree", cmd_commit_tree, RUN_SETUP },
diff --git a/git.spec.in b/git.spec.in
index f0746ed78c79739885ca9c32af27d3ee9ed4a974..556bdda7aa2ec7a99c7668390c60cdc69eed74f5 100644 (file)
--- a/git.spec.in
+++ b/git.spec.in
%build
make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" WITH_P4IMPORT=YesPlease \
+ ETC_GITCONFIG=/etc/gitconfig \
prefix=%{_prefix} PYTHON_PATH=%{python_path} all %{!?_without_docs: doc}
%install
rm -rf $RPM_BUILD_ROOT
make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" DESTDIR=$RPM_BUILD_ROOT \
WITH_P4IMPORT=YesPlease prefix=%{_prefix} mandir=%{_mandir} \
+ ETC_GITCONFIG=/etc/gitconfig \
PYTHON_PATH=%{python_path} \
INSTALLDIRS=vendor install %{!?_without_docs: install-doc}
find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} ';'
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
index 5e402924040d55aff5fb831a737d108d1e68a0bb..2b023bd98a2def18f8a91c95b14fb5aadcbf9a0c 100644 (file)
--- a/gitweb/gitweb.css
+++ b/gitweb/gitweb.css
color: #cccccc;
}
+div.diff.nodifferences {
+ font-weight: bold;
+ color: #600000;
+}
div.index_include {
border: solid #d9d8d1;
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index c48b35aa39fd13c14baac33b8df66b9c9bfc1735..cbd8d03e64baa6324192e0be8a910c508b196d05 100755 (executable)
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
my ($fd, $difftree, $hash, $hash_parent) = @_;
my $patch_idx = 0;
+ my $patch_number = 0;
my $patch_line;
my $diffinfo;
my (%from, %to);
# git diff header
#assert($patch_line =~ m/^diff /) if DEBUG;
#assert($patch_line !~ m!$/$!) if DEBUG; # is chomp-ed
+ $patch_number++;
push @diff_header, $patch_line;
# extended diff header
} continue {
print "</div>\n"; # class="patch"
}
+ print "<div class=\"diff nodifferences\">No differences found</div>\n" if (!$patch_number);
print "</div>\n"; # class="patchset"
}
diff --git a/index-pack.c b/index-pack.c
index 3c768fbc631387b59bbbae3423b65b6a311a702b..824004f9a2a53dcef1db3c083a4a94ce0eff9028 100644 (file)
--- a/index-pack.c
+++ b/index-pack.c
#include "commit.h"
#include "tag.h"
#include "tree.h"
+#include "progress.h"
static const char index_pack_usage[] =
"git-index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
struct object_entry
{
- unsigned long offset;
+ off_t offset;
unsigned long size;
unsigned int hdr_size;
+ uint32_t crc32;
enum object_type type;
enum object_type real_type;
unsigned char sha1[20];
union delta_base {
unsigned char sha1[20];
- unsigned long offset;
+ off_t offset;
};
/*
static int from_stdin;
static int verbose;
-static volatile sig_atomic_t progress_update;
-
-static void progress_interval(int signum)
-{
- progress_update = 1;
-}
-
-static void setup_progress_signal(void)
-{
- struct sigaction sa;
- struct itimerval v;
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = progress_interval;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
- sigaction(SIGALRM, &sa, NULL);
-
- v.it_interval.tv_sec = 1;
- v.it_interval.tv_usec = 0;
- v.it_value = v.it_interval;
- setitimer(ITIMER_REAL, &v, NULL);
-
-}
-
-static unsigned display_progress(unsigned n, unsigned total, unsigned last_pc)
-{
- unsigned percent = n * 100 / total;
- if (percent != last_pc || progress_update) {
- fprintf(stderr, "%4u%% (%u/%u) done\r", percent, n, total);
- progress_update = 0;
- }
- return percent;
-}
+static struct progress progress;
/* We always read in 4kB chunks. */
static unsigned char input_buffer[4096];
-static unsigned long input_offset, input_len, consumed_bytes;
+static unsigned int input_offset, input_len;
+static off_t consumed_bytes;
static SHA_CTX input_ctx;
+static uint32_t input_crc32;
static int input_fd, output_fd, pack_fd;
/* Discard current buffer used content. */
{
if (bytes > input_len)
die("used more bytes than were available");
+ input_crc32 = crc32(input_crc32, input_buffer + input_offset, bytes);
input_len -= bytes;
input_offset += bytes;
+
+ /* make sure off_t is sufficiently large not to wrap */
+ if (consumed_bytes > consumed_bytes + bytes)
+ die("pack too large for current definition of off_t");
consumed_bytes += bytes;
}
static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_base)
{
unsigned char *p, c;
- unsigned long size, base_offset;
+ unsigned long size;
+ off_t base_offset;
unsigned shift;
+ void *data;
obj->offset = consumed_bytes;
+ input_crc32 = crc32(0, Z_NULL, 0);
p = fill(1);
c = *p;
@@ -249,7 +228,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
base_offset = c & 127;
while (c & 128) {
base_offset += 1;
- if (!base_offset || base_offset & ~(~0UL >> 7))
+ if (!base_offset || MSB(base_offset, 7))
bad_object(obj->offset, "offset value overflow for delta base object");
p = fill(1);
c = *p;
@@ -270,7 +249,9 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
}
obj->hdr_size = consumed_bytes - obj->offset;
- return unpack_entry_data(obj->offset, obj->size);
+ data = unpack_entry_data(obj->offset, obj->size);
+ obj->crc32 = input_crc32;
+ return data;
}
static void *get_data_from_pack(struct object_entry *obj)
/* Parse all objects and return the pack content SHA1 hash */
static void parse_pack_objects(unsigned char *sha1)
{
- int i, percent = -1;
+ int i;
struct delta_entry *delta = deltas;
void *data;
struct stat st;
* - remember base (SHA1 or offset) for all deltas.
*/
if (verbose)
- fprintf(stderr, "Indexing %d objects.\n", nr_objects);
+ start_progress(&progress, "Indexing %u objects...", "", nr_objects);
for (i = 0; i < nr_objects; i++) {
struct object_entry *obj = &objects[i];
data = unpack_raw_entry(obj, &delta->base);
sha1_object(data, obj->size, obj->type, obj->sha1);
free(data);
if (verbose)
- percent = display_progress(i+1, nr_objects, percent);
+ display_progress(&progress, i+1);
}
objects[i].offset = consumed_bytes;
if (verbose)
- fputc('\n', stderr);
+ stop_progress(&progress);
/* Check pack integrity */
flush();
* for some more deltas.
*/
if (verbose)
- fprintf(stderr, "Resolving %d deltas.\n", nr_deltas);
+ start_progress(&progress, "Resolving %u deltas...", "", nr_deltas);
for (i = 0; i < nr_objects; i++) {
struct object_entry *obj = &objects[i];
union delta_base base;
}
free(data);
if (verbose)
- percent = display_progress(nr_resolved_deltas,
- nr_deltas, percent);
+ display_progress(&progress, nr_resolved_deltas);
}
- if (verbose && nr_resolved_deltas == nr_deltas)
- fputc('\n', stderr);
}
-static int write_compressed(int fd, void *in, unsigned int size)
+static int write_compressed(int fd, void *in, unsigned int size, uint32_t *obj_crc)
{
z_stream stream;
unsigned long maxsize;
size = stream.total_out;
write_or_die(fd, out, size);
+ *obj_crc = crc32(*obj_crc, out, size);
free(out);
return size;
}
}
header[n++] = c;
write_or_die(output_fd, header, n);
+ obj[0].crc32 = crc32(0, Z_NULL, 0);
+ obj[0].crc32 = crc32(obj[0].crc32, header, n);
obj[1].offset = obj[0].offset + n;
- obj[1].offset += write_compressed(output_fd, buf, size);
+ obj[1].offset += write_compressed(output_fd, buf, size, &obj[0].crc32);
hashcpy(obj->sha1, sha1);
}
static void fix_unresolved_deltas(int nr_unresolved)
{
struct delta_entry **sorted_by_pos;
- int i, n = 0, percent = -1;
+ int i, n = 0;
/*
* Since many unresolved deltas may well be themselves base objects
append_obj_to_pack(d->base.sha1, data, size, type);
free(data);
if (verbose)
- percent = display_progress(nr_resolved_deltas,
- nr_deltas, percent);
+ display_progress(&progress, nr_resolved_deltas);
}
free(sorted_by_pos);
- if (verbose)
- fputc('\n', stderr);
}
static void readjust_pack_header_and_sha1(unsigned char *sha1)
write_or_die(output_fd, sha1, 20);
}
+static uint32_t index_default_version = 1;
+static uint32_t index_off32_limit = 0x7fffffff;
+
static int sha1_compare(const void *_a, const void *_b)
{
struct object_entry *a = *(struct object_entry **)_a;
@@ -670,9 +651,10 @@ static const char *write_index_file(const char *index_name, unsigned char *sha1)
{
struct sha1file *f;
struct object_entry **sorted_by_sha, **list, **last;
- unsigned int array[256];
+ uint32_t array[256];
int i, fd;
SHA_CTX ctx;
+ uint32_t index_version;
if (nr_objects) {
sorted_by_sha =
@@ -683,7 +665,6 @@ static const char *write_index_file(const char *index_name, unsigned char *sha1)
sorted_by_sha[i] = &objects[i];
qsort(sorted_by_sha, nr_objects, sizeof(sorted_by_sha[0]),
sha1_compare);
-
}
else
sorted_by_sha = list = last = NULL;
@@ -702,6 +683,17 @@ static const char *write_index_file(const char *index_name, unsigned char *sha1)
die("unable to create %s: %s", index_name, strerror(errno));
f = sha1fd(fd, index_name);
+ /* if last object's offset is >= 2^31 we should use index V2 */
+ index_version = (objects[nr_objects-1].offset >> 31) ? 2 : index_default_version;
+
+ /* index versions 2 and above need a header */
+ if (index_version >= 2) {
+ struct pack_idx_header hdr;
+ hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
+ hdr.idx_version = htonl(index_version);
+ sha1write(f, &hdr, sizeof(hdr));
+ }
+
/*
* Write the first-level table (the list is sorted,
* but we use a 256-entry lookup to be able to avoid
@@ -718,24 +710,61 @@ static const char *write_index_file(const char *index_name, unsigned char *sha1)
array[i] = htonl(next - sorted_by_sha);
list = next;
}
- sha1write(f, array, 256 * sizeof(int));
+ sha1write(f, array, 256 * 4);
- /* recompute the SHA1 hash of sorted object names.
- * currently pack-objects does not do this, but that
- * can be fixed.
- */
+ /* compute the SHA1 hash of sorted object names. */
SHA1_Init(&ctx);
+
/*
* Write the actual SHA1 entries..
*/
list = sorted_by_sha;
for (i = 0; i < nr_objects; i++) {
struct object_entry *obj = *list++;
- unsigned int offset = htonl(obj->offset);
- sha1write(f, &offset, 4);
+ if (index_version < 2) {
+ uint32_t offset = htonl(obj->offset);
+ sha1write(f, &offset, 4);
+ }
sha1write(f, obj->sha1, 20);
SHA1_Update(&ctx, obj->sha1, 20);
}
+
+ if (index_version >= 2) {
+ unsigned int nr_large_offset = 0;
+
+ /* write the crc32 table */
+ list = sorted_by_sha;
+ for (i = 0; i < nr_objects; i++) {
+ struct object_entry *obj = *list++;
+ uint32_t crc32_val = htonl(obj->crc32);
+ sha1write(f, &crc32_val, 4);
+ }
+
+ /* write the 32-bit offset table */
+ list = sorted_by_sha;
+ for (i = 0; i < nr_objects; i++) {
+ struct object_entry *obj = *list++;
+ uint32_t offset = (obj->offset <= index_off32_limit) ?
+ obj->offset : (0x80000000 | nr_large_offset++);
+ offset = htonl(offset);
+ sha1write(f, &offset, 4);
+ }
+
+ /* write the large offset table */
+ list = sorted_by_sha;
+ while (nr_large_offset) {
+ struct object_entry *obj = *list++;
+ uint64_t offset = obj->offset;
+ if (offset > index_off32_limit) {
+ uint32_t split[2];
+ split[0] = htonl(offset >> 32);
+ split[1] = htonl(offset & 0xffffffff);
+ sha1write(f, split, 8);
+ nr_large_offset--;
+ }
+ }
+ }
+
sha1write(f, sha1, 20);
sha1close(f, NULL, 1);
free(sorted_by_sha);
if (index_name || (i+1) >= argc)
usage(index_pack_usage);
index_name = argv[++i];
+ } else if (!prefixcmp(arg, "--index-version=")) {
+ char *c;
+ index_default_version = strtoul(arg + 16, &c, 10);
+ if (index_default_version > 2)
+ die("bad %s", arg);
+ if (*c == ',')
+ index_off32_limit = strtoul(c+1, &c, 0);
+ if (*c || index_off32_limit & 0x80000000)
+ die("bad %s", arg);
} else
usage(index_pack_usage);
continue;
parse_pack_header();
objects = xmalloc((nr_objects + 1) * sizeof(struct object_entry));
deltas = xmalloc(nr_objects * sizeof(struct delta_entry));
- if (verbose)
- setup_progress_signal();
parse_pack_objects(sha1);
- if (nr_deltas != nr_resolved_deltas) {
+ if (nr_deltas == nr_resolved_deltas) {
+ if (verbose)
+ stop_progress(&progress);
+ /* Flush remaining pack final 20-byte SHA1. */
+ flush();
+ } else {
if (fix_thin_pack) {
int nr_unresolved = nr_deltas - nr_resolved_deltas;
int nr_objects_initial = nr_objects;
(nr_objects + nr_unresolved + 1)
* sizeof(*objects));
fix_unresolved_deltas(nr_unresolved);
- if (verbose)
+ if (verbose) {
+ stop_progress(&progress);
fprintf(stderr, "%d objects were added to complete this thin pack.\n",
nr_objects - nr_objects_initial);
+ }
readjust_pack_header_and_sha1(sha1);
}
if (nr_deltas != nr_resolved_deltas)
die("pack has %d unresolved deltas",
nr_deltas - nr_resolved_deltas);
- } else {
- /* Flush remaining pack final 20-byte SHA1. */
- flush();
}
free(deltas);
curr_index = write_index_file(index_name, sha1);
diff --git a/list-objects.c b/list-objects.c
index 2ba2c958e0aac63f0d5b092019e71f6905edb052..310f8d39082a12d2c3daddd1fca454686e7425c3 100644 (file)
--- a/list-objects.c
+++ b/list-objects.c
add_object(obj, p, path, name);
}
+/*
+ * Processing a gitlink entry currently does nothing, since
+ * we do not recurse into the subproject.
+ *
+ * We *could* eventually add a flag that actually does that,
+ * which would involve:
+ * - is the subproject actually checked out?
+ * - if so, see if the subproject has already been added
+ * to the alternates list, and add it if not.
+ * - process the commit (or tag) the gitlink points to
+ * recursively.
+ *
+ * However, it's unclear whether there is really ever any
+ * reason to see superprojects and subprojects as such a
+ * "unified" object pool (potentially resulting in a totally
+ * humongous pack - avoiding which was the whole point of
+ * having gitlinks in the first place!).
+ *
+ * So for now, there is just a note that we *could* follow
+ * the link, and how to do it. Whether it necessarily makes
+ * any sense what-so-ever to ever do that is another issue.
+ */
+static void process_gitlink(struct rev_info *revs,
+ const unsigned char *sha1,
+ struct object_array *p,
+ struct name_path *path,
+ const char *name)
+{
+ /* Nothing to do */
+}
+
static void process_tree(struct rev_info *revs,
struct tree *tree,
struct object_array *p,
process_tree(revs,
lookup_tree(entry.sha1),
p, &me, entry.path);
+ else if (S_ISDIRLNK(entry.mode))
+ process_gitlink(revs, entry.sha1,
+ p, &me, entry.path);
else
process_blob(revs,
lookup_blob(entry.sha1),
diff --git a/lockfile.c b/lockfile.c
index bed6b21daf302c76cb87bb99b613f168df9899e1..23db35aff21d7c33197726128e2f120291e6e9f0 100644 (file)
--- a/lockfile.c
+++ b/lockfile.c
static void remove_lock_file(void)
{
+ pid_t me = getpid();
+
while (lock_file_list) {
- if (lock_file_list->filename[0])
+ if (lock_file_list->owner == me &&
+ lock_file_list->filename[0])
unlink(lock_file_list->filename);
lock_file_list = lock_file_list->next;
}
sprintf(lk->filename, "%s.lock", path);
fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
if (0 <= fd) {
+ lk->owner = getpid();
if (!lk->on_list) {
lk->next = lock_file_list;
lock_file_list = lk;
diff --git a/merge-recursive.c b/merge-recursive.c
index cea6c877171d5f9275007c164704f02b52e3a267..403a4c8bca3062c8fa586e369b3fd1308c919641 100644 (file)
--- a/merge-recursive.c
+++ b/merge-recursive.c
#include "unpack-trees.h"
#include "path-list.h"
#include "xdiff-interface.h"
+#include "interpolate.h"
+#include "attr.h"
static int subtree_merge;
static int call_depth = 0;
static int verbosity = 2;
static int buffer_output = 1;
-static int do_progress = 1;
-static unsigned last_percent;
-static unsigned merged_cnt;
-static unsigned total_cnt;
-static volatile sig_atomic_t progress_update;
static struct output_buffer *output_list, *output_end;
static int show (int v)
}
}
-static void progress_interval(int signum)
-{
- progress_update = 1;
-}
-
-static void setup_progress_signal(void)
-{
- struct sigaction sa;
- struct itimerval v;
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = progress_interval;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
- sigaction(SIGALRM, &sa, NULL);
-
- v.it_interval.tv_sec = 1;
- v.it_interval.tv_usec = 0;
- v.it_value = v.it_interval;
- setitimer(ITIMER_REAL, &v, NULL);
-}
-
-static void display_progress()
-{
- unsigned percent = total_cnt ? merged_cnt * 100 / total_cnt : 0;
- if (progress_update || percent != last_percent) {
- fprintf(stderr, "%4u%% (%u/%u) done\r",
- percent, merged_cnt, total_cnt);
- progress_update = 0;
- last_percent = percent;
- }
-}
-
static struct cache_entry *make_cache_entry(unsigned int mode,
const unsigned char *sha1, const char *path, int stage, int refresh)
{
int i;
unmerged->strdup_paths = 1;
- total_cnt += active_nr;
- for (i = 0; i < active_nr; i++, merged_cnt++) {
+ for (i = 0; i < active_nr; i++) {
struct path_list_item *item;
struct stage_data *e;
struct cache_entry *ce = active_cache[i];
- if (do_progress)
- display_progress();
if (!ce_stage(ce))
continue;
mm->size = size;
}
+/*
+ * Customizable low-level merge drivers support.
+ */
+
+struct ll_merge_driver;
+typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
+ const char *path,
+ mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ mmbuffer_t *result);
+
+struct ll_merge_driver {
+ const char *name;
+ const char *description;
+ ll_merge_fn fn;
+ const char *recursive;
+ struct ll_merge_driver *next;
+ char *cmdline;
+};
+
+/*
+ * Built-in low-levels
+ */
+static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
+ const char *path_unused,
+ mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ mmbuffer_t *result)
+{
+ xpparam_t xpp;
+
+ memset(&xpp, 0, sizeof(xpp));
+ return xdl_merge(orig,
+ src1, name1,
+ src2, name2,
+ &xpp, XDL_MERGE_ZEALOUS,
+ result);
+}
+
+static int ll_union_merge(const struct ll_merge_driver *drv_unused,
+ const char *path_unused,
+ mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ mmbuffer_t *result)
+{
+ char *src, *dst;
+ long size;
+ const int marker_size = 7;
+
+ int status = ll_xdl_merge(drv_unused, path_unused,
+ orig, src1, NULL, src2, NULL, result);
+ if (status <= 0)
+ return status;
+ size = result->size;
+ src = dst = result->ptr;
+ while (size) {
+ char ch;
+ if ((marker_size < size) &&
+ (*src == '<' || *src == '=' || *src == '>')) {
+ int i;
+ ch = *src;
+ for (i = 0; i < marker_size; i++)
+ if (src[i] != ch)
+ goto not_a_marker;
+ if (src[marker_size] != '\n')
+ goto not_a_marker;
+ src += marker_size + 1;
+ size -= marker_size + 1;
+ continue;
+ }
+ not_a_marker:
+ do {
+ ch = *src++;
+ *dst++ = ch;
+ size--;
+ } while (ch != '\n' && size);
+ }
+ result->size = dst - result->ptr;
+ return 0;
+}
+
+static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
+ const char *path_unused,
+ mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ mmbuffer_t *result)
+{
+ /*
+ * The tentative merge result is "ours" for the final round,
+ * or common ancestor for an internal merge. Still return
+ * "conflicted merge" status.
+ */
+ mmfile_t *stolen = index_only ? orig : src1;
+
+ result->ptr = stolen->ptr;
+ result->size = stolen->size;
+ stolen->ptr = NULL;
+ return 1;
+}
+
+#define LL_BINARY_MERGE 0
+#define LL_TEXT_MERGE 1
+#define LL_UNION_MERGE 2
+static struct ll_merge_driver ll_merge_drv[] = {
+ { "binary", "built-in binary merge", ll_binary_merge },
+ { "text", "built-in 3-way text merge", ll_xdl_merge },
+ { "union", "built-in union merge", ll_union_merge },
+};
+
+static void create_temp(mmfile_t *src, char *path)
+{
+ int fd;
+
+ strcpy(path, ".merge_file_XXXXXX");
+ fd = mkstemp(path);
+ if (fd < 0)
+ die("unable to create temp-file");
+ if (write_in_full(fd, src->ptr, src->size) != src->size)
+ die("unable to write temp-file");
+ close(fd);
+}
+
+/*
+ * User defined low-level merge driver support.
+ */
+static int ll_ext_merge(const struct ll_merge_driver *fn,
+ const char *path,
+ mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ mmbuffer_t *result)
+{
+ char temp[3][50];
+ char cmdbuf[2048];
+ struct interp table[] = {
+ { "%O" },
+ { "%A" },
+ { "%B" },
+ };
+ struct child_process child;
+ const char *args[20];
+ int status, fd, i;
+ struct stat st;
+
+ if (fn->cmdline == NULL)
+ die("custom merge driver %s lacks command line.", fn->name);
+
+ result->ptr = NULL;
+ result->size = 0;
+ create_temp(orig, temp[0]);
+ create_temp(src1, temp[1]);
+ create_temp(src2, temp[2]);
+
+ interp_set_entry(table, 0, temp[0]);
+ interp_set_entry(table, 1, temp[1]);
+ interp_set_entry(table, 2, temp[2]);
+
+ output(1, "merging %s using %s", path,
+ fn->description ? fn->description : fn->name);
+
+ interpolate(cmdbuf, sizeof(cmdbuf), fn->cmdline, table, 3);
+
+ memset(&child, 0, sizeof(child));
+ child.argv = args;
+ args[0] = "sh";
+ args[1] = "-c";
+ args[2] = cmdbuf;
+ args[3] = NULL;
+
+ status = run_command(&child);
+ if (status < -ERR_RUN_COMMAND_FORK)
+ ; /* failure in run-command */
+ else
+ status = -status;
+ fd = open(temp[1], O_RDONLY);
+ if (fd < 0)
+ goto bad;
+ if (fstat(fd, &st))
+ goto close_bad;
+ result->size = st.st_size;
+ result->ptr = xmalloc(result->size + 1);
+ if (read_in_full(fd, result->ptr, result->size) != result->size) {
+ free(result->ptr);
+ result->ptr = NULL;
+ result->size = 0;
+ }
+ close_bad:
+ close(fd);
+ bad:
+ for (i = 0; i < 3; i++)
+ unlink(temp[i]);
+ return status;
+}
+
+/*
+ * merge.default and merge.driver configuration items
+ */
+static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
+static const char *default_ll_merge;
+
+static int read_merge_config(const char *var, const char *value)
+{
+ struct ll_merge_driver *fn;
+ const char *ep, *name;
+ int namelen;
+
+ if (!strcmp(var, "merge.default")) {
+ if (value)
+ default_ll_merge = strdup(value);
+ return 0;
+ }
+
+ /*
+ * We are not interested in anything but "merge.<name>.variable";
+ * especially, we do not want to look at variables such as
+ * "merge.summary", "merge.tool", and "merge.verbosity".
+ */
+ if (prefixcmp(var, "merge.") || (ep = strrchr(var, '.')) == var + 5)
+ return 0;
+
+ /*
+ * Find existing one as we might be processing merge.<name>.var2
+ * after seeing merge.<name>.var1.
+ */
+ name = var + 6;
+ namelen = ep - name;
+ for (fn = ll_user_merge; fn; fn = fn->next)
+ if (!strncmp(fn->name, name, namelen) && !fn->name[namelen])
+ break;
+ if (!fn) {
+ char *namebuf;
+ fn = xcalloc(1, sizeof(struct ll_merge_driver));
+ namebuf = xmalloc(namelen + 1);
+ memcpy(namebuf, name, namelen);
+ namebuf[namelen] = 0;
+ fn->name = namebuf;
+ fn->fn = ll_ext_merge;
+ fn->next = NULL;
+ *ll_user_merge_tail = fn;
+ ll_user_merge_tail = &(fn->next);
+ }
+
+ ep++;
+
+ if (!strcmp("name", ep)) {
+ if (!value)
+ return error("%s: lacks value", var);
+ fn->description = strdup(value);
+ return 0;
+ }
+
+ if (!strcmp("driver", ep)) {
+ if (!value)
+ return error("%s: lacks value", var);
+ /*
+ * merge.<name>.driver specifies the command line:
+ *
+ * command-line
+ *
+ * The command-line will be interpolated with the following
+ * tokens and is given to the shell:
+ *
+ * %O - temporary file name for the merge base.
+ * %A - temporary file name for our version.
+ * %B - temporary file name for the other branches' version.
+ *
+ * The external merge driver should write the results in the
+ * file named by %A, and signal that it has done with zero exit
+ * status.
+ */
+ fn->cmdline = strdup(value);
+ return 0;
+ }
+
+ if (!strcmp("recursive", ep)) {
+ if (!value)
+ return error("%s: lacks value", var);
+ fn->recursive = strdup(value);
+ return 0;
+ }
+
+ return 0;
+}
+
+static void initialize_ll_merge(void)
+{
+ if (ll_user_merge_tail)
+ return;
+ ll_user_merge_tail = &ll_user_merge;
+ git_config(read_merge_config);
+}
+
+static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr)
+{
+ struct ll_merge_driver *fn;
+ const char *name;
+ int i;
+
+ initialize_ll_merge();
+
+ if (ATTR_TRUE(merge_attr))
+ return &ll_merge_drv[LL_TEXT_MERGE];
+ else if (ATTR_FALSE(merge_attr))
+ return &ll_merge_drv[LL_BINARY_MERGE];
+ else if (ATTR_UNSET(merge_attr)) {
+ if (!default_ll_merge)
+ return &ll_merge_drv[LL_TEXT_MERGE];
+ else
+ name = default_ll_merge;
+ }
+ else
+ name = merge_attr;
+
+ for (fn = ll_user_merge; fn; fn = fn->next)
+ if (!strcmp(fn->name, name))
+ return fn;
+
+ for (i = 0; i < ARRAY_SIZE(ll_merge_drv); i++)
+ if (!strcmp(ll_merge_drv[i].name, name))
+ return &ll_merge_drv[i];
+
+ /* default to the 3-way */
+ return &ll_merge_drv[LL_TEXT_MERGE];
+}
+
+static const char *git_path_check_merge(const char *path)
+{
+ static struct git_attr_check attr_merge_check;
+
+ if (!attr_merge_check.attr)
+ attr_merge_check.attr = git_attr("merge", 5);
+
+ if (git_checkattr(path, 1, &attr_merge_check))
+ return NULL;
+ return attr_merge_check.value;
+}
+
+static int ll_merge(mmbuffer_t *result_buf,
+ struct diff_filespec *o,
+ struct diff_filespec *a,
+ struct diff_filespec *b,
+ const char *branch1,
+ const char *branch2)
+{
+ mmfile_t orig, src1, src2;
+ char *name1, *name2;
+ int merge_status;
+ const char *ll_driver_name;
+ const struct ll_merge_driver *driver;
+
+ name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
+ name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
+
+ fill_mm(o->sha1, &orig);
+ fill_mm(a->sha1, &src1);
+ fill_mm(b->sha1, &src2);
+
+ ll_driver_name = git_path_check_merge(a->path);
+ driver = find_ll_merge_driver(ll_driver_name);
+
+ if (index_only && driver->recursive)
+ driver = find_ll_merge_driver(driver->recursive);
+ merge_status = driver->fn(driver, a->path,
+ &orig, &src1, name1, &src2, name2,
+ result_buf);
+
+ free(name1);
+ free(name2);
+ free(orig.ptr);
+ free(src1.ptr);
+ free(src2.ptr);
+ return merge_status;
+}
+
static struct merge_file_info merge_file(struct diff_filespec *o,
struct diff_filespec *a, struct diff_filespec *b,
const char *branch1, const char *branch2)
else if (sha_eq(b->sha1, o->sha1))
hashcpy(result.sha, a->sha1);
else if (S_ISREG(a->mode)) {
- mmfile_t orig, src1, src2;
mmbuffer_t result_buf;
- xpparam_t xpp;
- char *name1, *name2;
int merge_status;
- name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
- name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
-
- fill_mm(o->sha1, &orig);
- fill_mm(a->sha1, &src1);
- fill_mm(b->sha1, &src2);
-
- memset(&xpp, 0, sizeof(xpp));
- merge_status = xdl_merge(&orig,
- &src1, name1,
- &src2, name2,
- &xpp, XDL_MERGE_ZEALOUS,
- &result_buf);
- free(name1);
- free(name2);
- free(orig.ptr);
- free(src1.ptr);
- free(src2.ptr);
+ merge_status = ll_merge(&result_buf, o, a, b,
+ branch1, branch2);
if ((merge_status < 0) || !result_buf.ptr)
die("Failed to execute internal merge");
re_merge = get_renames(merge, common, head, merge, entries);
clean = process_renames(re_head, re_merge,
branch1, branch2);
- total_cnt += entries->nr;
- for (i = 0; i < entries->nr; i++, merged_cnt++) {
+ for (i = 0; i < entries->nr; i++) {
const char *path = entries->items[i].path;
struct stage_data *e = entries->items[i].util;
if (!e->processed
&& !process_entry(path, e, branch1, branch2))
clean = 0;
- if (do_progress)
- display_progress();
}
path_list_clear(re_merge, 0);
commit_list_insert(h1, &(*result)->parents);
commit_list_insert(h2, &(*result)->parents->next);
}
- if (!call_depth && do_progress) {
- /* Make sure we end at 100% */
- if (!total_cnt)
- total_cnt = 1;
- merged_cnt = total_cnt;
- progress_update = 1;
- display_progress();
- fputc('\n', stderr);
- }
flush_output();
return clean;
}
}
if (argc - i != 3) /* "--" "<head>" "<remote>" */
die("Not handling anything other than two heads merge.");
- if (verbosity >= 5) {
+ if (verbosity >= 5)
buffer_output = 0;
- do_progress = 0;
- }
- else
- do_progress = isatty(1);
branch1 = argv[++i];
branch2 = argv[++i];
branch1 = better_branch_name(branch1);
branch2 = better_branch_name(branch2);
- if (do_progress)
- setup_progress_signal();
if (show(3))
printf("Merging %s with %s\n", branch1, branch2);
diff --git a/object.c b/object.c
index 78a44a6ef4e4823487861c9173f3db4a3fb76e3a..7bd3fec55655584f9cf4aebc6a13de7b84a78af5 100644 (file)
--- a/object.c
+++ b/object.c
obj_hash_size = new_hash_size;
}
-void created_object(const unsigned char *sha1, struct object *obj)
+void *create_object(const unsigned char *sha1, int type, void *o)
{
+ struct object *obj = o;
+
obj->parsed = 0;
obj->used = 0;
- obj->type = OBJ_NONE;
+ obj->type = type;
obj->flags = 0;
hashcpy(obj->sha1, sha1);
insert_obj_hash(obj, obj_hash, obj_hash_size);
nr_objs++;
+ return obj;
}
-union any_object {
- struct object object;
- struct commit commit;
- struct tree tree;
- struct blob blob;
- struct tag tag;
-};
-
struct object *lookup_unknown_object(const unsigned char *sha1)
{
struct object *obj = lookup_object(sha1);
- if (!obj) {
- union any_object *ret = xcalloc(1, sizeof(*ret));
- created_object(sha1, &ret->object);
- ret->object.type = OBJ_NONE;
- return &ret->object;
- }
+ if (!obj)
+ obj = create_object(sha1, OBJ_NONE, alloc_object_node());
return obj;
}
diff --git a/object.h b/object.h
index bdbbc1889c0227519de0ba02439f0e94dfcd1651..3e26a0e8b9f87526dc02841031e5544a2518c266 100644 (file)
--- a/object.h
+++ b/object.h
/** Internal only **/
struct object *lookup_object(const unsigned char *sha1);
-void created_object(const unsigned char *sha1, struct object *obj);
+extern void *create_object(const unsigned char *sha1, int type, void *obj);
/** Returns the object, having parsed it to find out what it is. **/
struct object *parse_object(const unsigned char *sha1);
diff --git a/pack-check.c b/pack-check.c
index f58083d11e0cfb974861d340bdea4ae18d2469e8..d04536bbff7cba22ca67521d45e690dfa5aa8675 100644 (file)
--- a/pack-check.c
+++ b/pack-check.c
* have verified that nr_objects matches between idx and pack,
* we do not do scan-streaming check on the pack file.
*/
- nr_objects = num_packed_objects(p);
+ nr_objects = p->num_objects;
for (i = 0, err = 0; i < nr_objects; i++) {
const unsigned char *sha1;
void *data;
{
uint32_t nr_objects, i, chain_histogram[MAX_CHAIN];
- nr_objects = num_packed_objects(p);
+ nr_objects = p->num_objects;
memset(chain_histogram, 0, sizeof(chain_histogram));
for (i = 0; i < nr_objects; i++) {
diff --git a/pack-redundant.c b/pack-redundant.c
index 40e579b2d9788bb0867b345c3596b1ad1539272a..87077e150c1b53a26464089a2cabafafb32e6636 100644 (file)
--- a/pack-redundant.c
+++ b/pack-redundant.c
static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
{
- int p1_off, p2_off;
+ unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step;
const unsigned char *p1_base, *p2_base;
struct llist_item *p1_hint = NULL, *p2_hint = NULL;
- p1_off = p2_off = 256 * 4 + 4;
p1_base = p1->pack->index_data;
p2_base = p2->pack->index_data;
+ p1_base += 256 * 4 + ((p1->pack->index_version < 2) ? 4 : 8);
+ p2_base += 256 * 4 + ((p2->pack->index_version < 2) ? 4 : 8);
+ p1_step = (p1->pack->index_version < 2) ? 24 : 20;
+ p2_step = (p2->pack->index_version < 2) ? 24 : 20;
- while (p1_off <= p1->pack->index_size - 3 * 20 &&
- p2_off <= p2->pack->index_size - 3 * 20)
+ while (p1_off < p1->pack->num_objects * p1_step &&
+ p2_off < p2->pack->num_objects * p2_step)
{
int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
/* cmp ~ p1 - p2 */
p1_base + p1_off, p1_hint);
p2_hint = llist_sorted_remove(p2->unique_objects,
p1_base + p1_off, p2_hint);
- p1_off+=24;
- p2_off+=24;
+ p1_off += p1_step;
+ p2_off += p2_step;
continue;
}
if (cmp < 0) { /* p1 has the object, p2 doesn't */
- p1_off+=24;
+ p1_off += p1_step;
} else { /* p2 has the object, p1 doesn't */
- p2_off+=24;
+ p2_off += p2_step;
}
}
}
static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
{
size_t ret = 0;
- int p1_off, p2_off;
+ unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step;
const unsigned char *p1_base, *p2_base;
- p1_off = p2_off = 256 * 4 + 4;
p1_base = p1->index_data;
p2_base = p2->index_data;
+ p1_base += 256 * 4 + ((p1->index_version < 2) ? 4 : 8);
+ p2_base += 256 * 4 + ((p2->index_version < 2) ? 4 : 8);
+ p1_step = (p1->index_version < 2) ? 24 : 20;
+ p2_step = (p2->index_version < 2) ? 24 : 20;
- while (p1_off <= p1->index_size - 3 * 20 &&
- p2_off <= p2->index_size - 3 * 20)
+ while (p1_off < p1->num_objects * p1_step &&
+ p2_off < p2->num_objects * p2_step)
{
int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
/* cmp ~ p1 - p2 */
if (cmp == 0) {
ret++;
- p1_off+=24;
- p2_off+=24;
+ p1_off += p1_step;
+ p2_off += p2_step;
continue;
}
if (cmp < 0) { /* p1 has the object, p2 doesn't */
- p1_off+=24;
+ p1_off += p1_step;
} else { /* p2 has the object, p1 doesn't */
- p2_off+=24;
+ p2_off += p2_step;
}
}
return ret;
static struct pack_list * add_pack(struct packed_git *p)
{
struct pack_list l;
- size_t off;
+ unsigned long off = 0, step;
const unsigned char *base;
if (!p->pack_local && !(alt_odb || verbose))
l.pack = p;
llist_init(&l.all_objects);
- off = 256 * 4 + 4;
base = p->index_data;
- while (off <= p->index_size - 3 * 20) {
+ base += 256 * 4 + ((p->index_version < 2) ? 4 : 8);
+ step = (p->index_version < 2) ? 24 : 20;
+ while (off < p->num_objects * step) {
llist_insert_back(l.all_objects, base + off);
- off += 24;
+ off += step;
}
/* this list will be pruned in cmp_two_packs later */
l.unique_objects = llist_copy(l.all_objects);
diff --git a/perl/Makefile.PL b/perl/Makefile.PL
index 9b117fd0d736615a8c4aeda742384956373d295a..437516142cb6c14f197dc5821635a6ff8bc91adf 100644 (file)
--- a/perl/Makefile.PL
+++ b/perl/Makefile.PL
# We come with our own bundled Error.pm. It's not in the set of default
# Perl modules so install it if it's not available on the system yet.
eval { require Error };
-if ($@) {
+if ($@ || $Error::VERSION < 0.15009) {
$pm{'private-Error.pm'} = '$(INST_LIBDIR)/Error.pm';
}
diff --git a/progress.c b/progress.c
--- /dev/null
+++ b/progress.c
@@ -0,0 +1,106 @@
+#include "git-compat-util.h"
+#include "progress.h"
+
+static volatile sig_atomic_t progress_update;
+
+static void progress_interval(int signum)
+{
+ progress_update = 1;
+}
+
+static void set_progress_signal(void)
+{
+ struct sigaction sa;
+ struct itimerval v;
+
+ progress_update = 0;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = progress_interval;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGALRM, &sa, NULL);
+
+ v.it_interval.tv_sec = 1;
+ v.it_interval.tv_usec = 0;
+ v.it_value = v.it_interval;
+ setitimer(ITIMER_REAL, &v, NULL);
+}
+
+static void clear_progress_signal(void)
+{
+ struct itimerval v = {{0,},};
+ setitimer(ITIMER_REAL, &v, NULL);
+ signal(SIGALRM, SIG_IGN);
+ progress_update = 0;
+}
+
+int display_progress(struct progress *progress, unsigned n)
+{
+ if (progress->delay) {
+ char buf[80];
+ if (!progress_update || --progress->delay)
+ return 0;
+ if (progress->total) {
+ unsigned percent = n * 100 / progress->total;
+ if (percent > progress->delayed_percent_treshold) {
+ /* inhibit this progress report entirely */
+ clear_progress_signal();
+ progress->delay = -1;
+ progress->total = 0;
+ return 0;
+ }
+ }
+ if (snprintf(buf, sizeof(buf),
+ progress->delayed_title, progress->total))
+ fprintf(stderr, "%s\n", buf);
+ }
+ if (progress->total) {
+ unsigned percent = n * 100 / progress->total;
+ if (percent != progress->last_percent || progress_update) {
+ progress->last_percent = percent;
+ fprintf(stderr, "%s%4u%% (%u/%u) done\r",
+ progress->prefix, percent, n, progress->total);
+ progress_update = 0;
+ return 1;
+ }
+ } else if (progress_update) {
+ fprintf(stderr, "%s%u\r", progress->prefix, n);
+ progress_update = 0;
+ return 1;
+ }
+ return 0;
+}
+
+void start_progress(struct progress *progress, const char *title,
+ const char *prefix, unsigned total)
+{
+ char buf[80];
+ progress->prefix = prefix;
+ progress->total = total;
+ progress->last_percent = -1;
+ progress->delay = 0;
+ if (snprintf(buf, sizeof(buf), title, total))
+ fprintf(stderr, "%s\n", buf);
+ set_progress_signal();
+}
+
+void start_progress_delay(struct progress *progress, const char *title,
+ const char *prefix, unsigned total,
+ unsigned percent_treshold, unsigned delay)
+{
+ progress->prefix = prefix;
+ progress->total = total;
+ progress->last_percent = -1;
+ progress->delayed_percent_treshold = percent_treshold;
+ progress->delayed_title = title;
+ progress->delay = delay;
+ set_progress_signal();
+}
+
+void stop_progress(struct progress *progress)
+{
+ clear_progress_signal();
+ if (progress->total)
+ fputc('\n', stderr);
+}
diff --git a/progress.h b/progress.h
--- /dev/null
+++ b/progress.h
@@ -0,0 +1,21 @@
+#ifndef __progress_h__
+#define __progress_h__
+
+struct progress {
+ const char *prefix;
+ unsigned total;
+ unsigned last_percent;
+ unsigned delay;
+ unsigned delayed_percent_treshold;
+ const char *delayed_title;
+};
+
+int display_progress(struct progress *progress, unsigned n);
+void start_progress(struct progress *progress, const char *title,
+ const char *prefix, unsigned total);
+void start_progress_delay(struct progress *progress, const char *title,
+ const char *prefix, unsigned total,
+ unsigned percent_treshold, unsigned delay);
+void stop_progress(struct progress *progress);
+
+#endif
diff --git a/read-cache.c b/read-cache.c
index 54573ce2ee3b2c70d5419716b20ade61683bc289..d2f332a6222cc3543bde8fa661c54c5c904cf561 100644 (file)
--- a/read-cache.c
+++ b/read-cache.c
*/
#include "cache.h"
#include "cache-tree.h"
+#include "refs.h"
/* Index extensions.
*
return match;
}
+static int ce_compare_gitlink(struct cache_entry *ce)
+{
+ unsigned char sha1[20];
+
+ /*
+ * We don't actually require that the .git directory
+ * under DIRLNK directory be a valid git directory. It
+ * might even be missing (in case nobody populated that
+ * sub-project).
+ *
+ * If so, we consider it always to match.
+ */
+ if (resolve_gitlink_ref(ce->name, "HEAD", sha1) < 0)
+ return 0;
+ return hashcmp(sha1, ce->sha1);
+}
+
static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
{
switch (st->st_mode & S_IFMT) {
if (ce_compare_link(ce, xsize_t(st->st_size)))
return DATA_CHANGED;
break;
+ case S_IFDIR:
+ if (S_ISDIRLNK(ntohl(ce->ce_mode)))
+ return 0;
default:
return TYPE_CHANGED;
}
(has_symlinks || !S_ISREG(st->st_mode)))
changed |= TYPE_CHANGED;
break;
+ case S_IFDIRLNK:
+ if (!S_ISDIR(st->st_mode))
+ changed |= TYPE_CHANGED;
+ else if (ce_compare_gitlink(ce))
+ changed |= DATA_CHANGED;
+ return changed;
default:
die("internal error: ce_mode is %o", ntohl(ce->ce_mode));
}
if (lstat(path, &st))
die("%s: unable to stat (%s)", path, strerror(errno));
- if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
- die("%s: can only add regular files or symbolic links", path);
+ if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
+ die("%s: can only add regular files, symbolic links or git-directories", path);
namelen = strlen(path);
+ if (S_ISDIR(st.st_mode)) {
+ while (namelen && path[namelen-1] == '/')
+ namelen--;
+ }
size = cache_entry_size(namelen);
ce = xcalloc(1, size);
memcpy(ce->name, path, namelen);
index d7be2841c5f2a5fbee8964051b86356af7bf9905..89876bff871d007a6675f5790ce8cb34fe21fb39 100644 (file)
--- a/refs.c
+++ b/refs.c
struct ref_list **new_entry)
{
int len;
- struct ref_list **p = &list, *entry;
-
- /* Find the place to insert the ref into.. */
- while ((entry = *p) != NULL) {
- int cmp = strcmp(entry->name, name);
- if (cmp > 0)
- break;
-
- /* Same as existing entry? */
- if (!cmp) {
- if (new_entry)
- *new_entry = entry;
- return list;
- }
- p = &entry->next;
- }
+ struct ref_list *entry;
/* Allocate it and add it in.. */
len = strlen(name) + 1;
hashclr(entry->peeled);
memcpy(entry->name, name, len);
entry->flag = flag;
- entry->next = *p;
- *p = entry;
+ entry->next = list;
if (new_entry)
*new_entry = entry;
- return list;
+ return entry;
+}
+
+/* merge sort the ref list */
+static struct ref_list *sort_ref_list(struct ref_list *list)
+{
+ int psize, qsize, last_merge_count, cmp;
+ struct ref_list *p, *q, *l, *e;
+ struct ref_list *new_list = list;
+ int k = 1;
+ int merge_count = 0;
+
+ if (!list)
+ return list;
+
+ do {
+ last_merge_count = merge_count;
+ merge_count = 0;
+
+ psize = 0;
+
+ p = new_list;
+ q = new_list;
+ new_list = NULL;
+ l = NULL;
+
+ while (p) {
+ merge_count++;
+
+ while (psize < k && q->next) {
+ q = q->next;
+ psize++;
+ }
+ qsize = k;
+
+ while ((psize > 0) || (qsize > 0 && q)) {
+ if (qsize == 0 || !q) {
+ e = p;
+ p = p->next;
+ psize--;
+ } else if (psize == 0) {
+ e = q;
+ q = q->next;
+ qsize--;
+ } else {
+ cmp = strcmp(q->name, p->name);
+ if (cmp < 0) {
+ e = q;
+ q = q->next;
+ qsize--;
+ } else if (cmp > 0) {
+ e = p;
+ p = p->next;
+ psize--;
+ } else {
+ if (hashcmp(q->sha1, p->sha1))
+ die("Duplicated ref, and SHA1s don't match: %s",
+ q->name);
+ warning("Duplicated ref: %s", q->name);
+ e = q;
+ q = q->next;
+ qsize--;
+ free(e);
+ e = p;
+ p = p->next;
+ psize--;
+ }
+ }
+
+ e->next = NULL;
+
+ if (l)
+ l->next = e;
+ if (!new_list)
+ new_list = e;
+ l = e;
+ }
+
+ p = q;
+ };
+
+ k = k * 2;
+ } while ((last_merge_count != merge_count) || (last_merge_count != 1));
+
+ return new_list;
}
/*
!get_sha1_hex(refline + 1, sha1))
hashcpy(last->peeled, sha1);
}
- cached_refs->packed = list;
+ cached_refs->packed = sort_ref_list(list);
}
static struct ref_list *get_packed_refs(void)
free(ref);
closedir(dir);
}
- return list;
+ return sort_ref_list(list);
}
static struct ref_list *get_loose_refs(void)
/* We allow "recursive" symbolic refs. Only within reason, though */
#define MAXDEPTH 5
+#define MAXREFLEN (1024)
+
+static int resolve_gitlink_packed_ref(char *name, int pathlen, const char *refname, unsigned char *result)
+{
+ FILE *f;
+ struct cached_refs refs;
+ struct ref_list *ref;
+ int retval;
+
+ strcpy(name + pathlen, "packed-refs");
+ f = fopen(name, "r");
+ if (!f)
+ return -1;
+ read_packed_refs(f, &refs);
+ fclose(f);
+ ref = refs.packed;
+ retval = -1;
+ while (ref) {
+ if (!strcmp(ref->name, refname)) {
+ retval = 0;
+ memcpy(result, ref->sha1, 20);
+ break;
+ }
+ ref = ref->next;
+ }
+ free_ref_list(refs.packed);
+ return retval;
+}
+
+static int resolve_gitlink_ref_recursive(char *name, int pathlen, const char *refname, unsigned char *result, int recursion)
+{
+ int fd, len = strlen(refname);
+ char buffer[128], *p;
+
+ if (recursion > MAXDEPTH || len > MAXREFLEN)
+ return -1;
+ memcpy(name + pathlen, refname, len+1);
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ return resolve_gitlink_packed_ref(name, pathlen, refname, result);
+
+ len = read(fd, buffer, sizeof(buffer)-1);
+ close(fd);
+ if (len < 0)
+ return -1;
+ while (len && isspace(buffer[len-1]))
+ len--;
+ buffer[len] = 0;
+
+ /* Was it a detached head or an old-fashioned symlink? */
+ if (!get_sha1_hex(buffer, result))
+ return 0;
+
+ /* Symref? */
+ if (strncmp(buffer, "ref:", 4))
+ return -1;
+ p = buffer + 4;
+ while (isspace(*p))
+ p++;
+
+ return resolve_gitlink_ref_recursive(name, pathlen, p, result, recursion+1);
+}
+
+int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *result)
+{
+ int len = strlen(path), retval;
+ char *gitdir;
+
+ while (len && path[len-1] == '/')
+ len--;
+ if (!len)
+ return -1;
+ gitdir = xmalloc(len + MAXREFLEN + 8);
+ memcpy(gitdir, path, len);
+ memcpy(gitdir + len, "/.git/", 7);
+
+ retval = resolve_gitlink_ref_recursive(gitdir, len+6, refname, result, 0);
+ free(gitdir);
+ return retval;
+}
const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
{
index acedffc0e412e1de6137d665a7c6b32f58b1c20b..f61f6d934e80b21432f93cd3b9f138770a9d2b86 100644 (file)
--- a/refs.h
+++ b/refs.h
/** rename ref, return 0 on success **/
extern int rename_ref(const char *oldref, const char *newref, const char *logmsg);
+/** resolve ref in nested "gitlink" repository */
+extern int resolve_gitlink_ref(const char *name, const char *refname, unsigned char *result);
+
#endif /* REFS_H */
diff --git a/sha1_file.c b/sha1_file.c
index 4304fe9bbc2b8e796e944fa7ddb2ea2791000adf..06426640eae9db23a0e68e28b74402662fc4da87 100644 (file)
--- a/sha1_file.c
+++ b/sha1_file.c
#include "commit.h"
#include "tag.h"
#include "tree.h"
+#include "refs.h"
#ifndef O_NOATIME
#if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
void *idx_map;
struct pack_idx_header *hdr;
size_t idx_size;
- uint32_t nr, i, *index;
+ uint32_t version, nr, i, *index;
int fd = open(path, O_RDONLY);
struct stat st;
idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
- /* a future index format would start with this, as older git
- * binaries would fail the non-monotonic index check below.
- * give a nicer warning to the user if we can.
- */
hdr = idx_map;
if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
- munmap(idx_map, idx_size);
- return error("index file %s is a newer version"
- " and is not supported by this binary"
- " (try upgrading GIT to a newer version)",
- path);
- }
+ version = ntohl(hdr->idx_version);
+ if (version < 2 || version > 2) {
+ munmap(idx_map, idx_size);
+ return error("index file %s is version %d"
+ " and is not supported by this binary"
+ " (try upgrading GIT to a newer version)",
+ path, version);
+ }
+ } else
+ version = 1;
nr = 0;
index = idx_map;
+ if (version > 1)
+ index += 2; /* skip index header */
for (i = 0; i < 256; i++) {
uint32_t n = ntohl(index[i]);
if (n < nr) {
nr = n;
}
- /*
- * Total size:
- * - 256 index entries 4 bytes each
- * - 24-byte entries * nr (20-byte sha1 + 4-byte offset)
- * - 20-byte SHA1 of the packfile
- * - 20-byte SHA1 file checksum
- */
- if (idx_size != 4*256 + nr * 24 + 20 + 20) {
- munmap(idx_map, idx_size);
- return error("wrong index file size in %s", path);
+ if (version == 1) {
+ /*
+ * Total size:
+ * - 256 index entries 4 bytes each
+ * - 24-byte entries * nr (20-byte sha1 + 4-byte offset)
+ * - 20-byte SHA1 of the packfile
+ * - 20-byte SHA1 file checksum
+ */
+ if (idx_size != 4*256 + nr * 24 + 20 + 20) {
+ munmap(idx_map, idx_size);
+ return error("wrong index file size in %s", path);
+ }
+ } else if (version == 2) {
+ /*
+ * Minimum size:
+ * - 8 bytes of header
+ * - 256 index entries 4 bytes each
+ * - 20-byte sha1 entry * nr
+ * - 4-byte crc entry * nr
+ * - 4-byte offset entry * nr
+ * - 20-byte SHA1 of the packfile
+ * - 20-byte SHA1 file checksum
+ * And after the 4-byte offset table might be a
+ * variable sized table containing 8-byte entries
+ * for offsets larger than 2^31.
+ */
+ unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20;
+ if (idx_size < min_size || idx_size > min_size + (nr - 1)*8) {
+ munmap(idx_map, idx_size);
+ return error("wrong index file size in %s", path);
+ }
+ if (idx_size != min_size) {
+ /* make sure we can deal with large pack offsets */
+ off_t x = 0x7fffffffUL, y = 0xffffffffUL;
+ if (x > (x + 1) || y > (y + 1)) {
+ munmap(idx_map, idx_size);
+ return error("pack too large for current definition of off_t in %s", path);
+ }
+ }
}
- p->index_version = 1;
+ p->index_version = version;
p->index_data = idx_map;
p->index_size = idx_size;
+ p->num_objects = nr;
return 0;
}
p->pack_name, ntohl(hdr.hdr_version));
/* Verify the pack matches its index. */
- if (num_packed_objects(p) != ntohl(hdr.hdr_entries))
+ if (p->num_objects != ntohl(hdr.hdr_entries))
return error("packfile %s claims to have %u objects"
- " while index size indicates %u objects",
- p->pack_name, ntohl(hdr.hdr_entries),
- num_packed_objects(p));
+ " while index indicates %u objects",
+ p->pack_name, ntohl(hdr.hdr_entries),
+ p->num_objects);
if (lseek(p->pack_fd, p->pack_size - sizeof(sha1), SEEK_SET) == -1)
return error("end of packfile %s is unavailable", p->pack_name);
if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
@@ -1128,6 +1161,43 @@ static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type
return unpack_sha1_rest(&stream, hdr, *size, sha1);
}
+unsigned long get_size_from_delta(struct packed_git *p,
+ struct pack_window **w_curs,
+ off_t curpos)
+{
+ const unsigned char *data;
+ unsigned char delta_head[20], *in;
+ z_stream stream;
+ int st;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.next_out = delta_head;
+ stream.avail_out = sizeof(delta_head);
+
+ inflateInit(&stream);
+ do {
+ in = use_pack(p, w_curs, curpos, &stream.avail_in);
+ stream.next_in = in;
+ st = inflate(&stream, Z_FINISH);
+ curpos += stream.next_in - in;
+ } while ((st == Z_OK || st == Z_BUF_ERROR) &&
+ stream.total_out < sizeof(delta_head));
+ inflateEnd(&stream);
+ if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head))
+ die("delta data unpack-initial failed");
+
+ /* Examine the initial part of the delta to figure out
+ * the result size.
+ */
+ data = delta_head;
+
+ /* ignore base size */
+ get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
+
+ /* Read the result size */
+ return get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
+}
+
static off_t get_delta_base(struct packed_git *p,
struct pack_window **w_curs,
off_t *curpos,
base_offset = c & 127;
while (c & 128) {
base_offset += 1;
- if (!base_offset || base_offset & ~(~0UL >> 7))
+ if (!base_offset || MSB(base_offset, 7))
die("offset value overflow for delta base object");
c = base_info[used++];
base_offset = (base_offset << 7) + (c & 127);
* based on a base with a wrong size. This saves tons of
* inflate() calls.
*/
- if (sizep) {
- const unsigned char *data;
- unsigned char delta_head[20], *in;
- z_stream stream;
- int st;
-
- memset(&stream, 0, sizeof(stream));
- stream.next_out = delta_head;
- stream.avail_out = sizeof(delta_head);
-
- inflateInit(&stream);
- do {
- in = use_pack(p, w_curs, curpos, &stream.avail_in);
- stream.next_in = in;
- st = inflate(&stream, Z_FINISH);
- curpos += stream.next_in - in;
- } while ((st == Z_OK || st == Z_BUF_ERROR)
- && stream.total_out < sizeof(delta_head));
- inflateEnd(&stream);
- if ((st != Z_STREAM_END) &&
- stream.total_out != sizeof(delta_head))
- die("delta data unpack-initial failed");
-
- /* Examine the initial part of the delta to figure out
- * the result size.
- */
- data = delta_head;
-
- /* ignore base size */
- get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
-
- /* Read the result size */
- *sizep = get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
- }
+ if (sizep)
+ *sizep = get_size_from_delta(p, w_curs, curpos);
return type;
}
return data;
}
-uint32_t num_packed_objects(const struct packed_git *p)
+const unsigned char *nth_packed_object_sha1(const struct packed_git *p,
+ uint32_t n)
{
- /* See check_packed_git_idx() */
- return (uint32_t)((p->index_size - 20 - 20 - 4*256) / 24);
+ const unsigned char *index = p->index_data;
+ if (n >= p->num_objects)
+ return NULL;
+ index += 4 * 256;
+ if (p->index_version == 1) {
+ return index + 24 * n + 4;
+ } else {
+ index += 8;
+ return index + 20 * n;
+ }
}
-const unsigned char *nth_packed_object_sha1(const struct packed_git *p,
- uint32_t n)
+static off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)
{
const unsigned char *index = p->index_data;
index += 4 * 256;
- if (num_packed_objects(p) <= n)
- return NULL;
- return index + 24 * n + 4;
+ if (p->index_version == 1) {
+ return ntohl(*((uint32_t *)(index + 24 * n)));
+ } else {
+ uint32_t off;
+ index += 8 + p->num_objects * (20 + 4);
+ off = ntohl(*((uint32_t *)(index + 4 * n)));
+ if (!(off & 0x80000000))
+ return off;
+ index += p->num_objects * 4 + (off & 0x7fffffff) * 8;
+ return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |
+ ntohl(*((uint32_t *)(index + 4)));
+ }
}
off_t find_pack_entry_one(const unsigned char *sha1,
struct packed_git *p)
{
const uint32_t *level1_ofs = p->index_data;
- int hi = ntohl(level1_ofs[*sha1]);
- int lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
const unsigned char *index = p->index_data;
+ unsigned hi, lo;
+ if (p->index_version > 1) {
+ level1_ofs += 2;
+ index += 8;
+ }
index += 4 * 256;
+ hi = ntohl(level1_ofs[*sha1]);
+ lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
do {
- int mi = (lo + hi) / 2;
- int cmp = hashcmp(index + 24 * mi + 4, sha1);
+ unsigned mi = (lo + hi) / 2;
+ unsigned x = (p->index_version > 1) ? (mi * 20) : (mi * 24 + 4);
+ int cmp = hashcmp(index + x, sha1);
if (!cmp)
- return ntohl(*((uint32_t *)((char *)index + (24 * mi))));
+ return nth_packed_object_offset(p, mi);
if (cmp > 0)
hi = mi;
else
*/
if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) {
unsigned long nsize = size;
- char *nbuf = buf;
- if (convert_to_git(path, &nbuf, &nsize)) {
- if (size)
- munmap(buf, size);
+ char *nbuf = convert_to_git(path, buf, &nsize);
+ if (nbuf) {
+ munmap(buf, size);
size = nsize;
buf = nbuf;
re_allocated = 1;
@@ -2332,6 +2392,8 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write
path);
free(target);
break;
+ case S_IFDIR:
+ return resolve_gitlink_ref(path, "HEAD", sha1);
default:
return error("%s: unsupported file type", path);
}
diff --git a/sha1_name.c b/sha1_name.c
index 267ea3f3edc63600bc1591d2115ee85222f3e8c5..b0b12bbe9de11403c5965518026433c8851e092b 100644 (file)
--- a/sha1_name.c
+++ b/sha1_name.c
prepare_packed_git();
for (p = packed_git; p && found < 2; p = p->next) {
- uint32_t num = num_packed_objects(p);
+ uint32_t num = p->num_objects;
uint32_t first = 0, last = num;
while (first < last) {
uint32_t mid = (first + last) / 2;
diff --git a/show-index.c b/show-index.c
index a30a2de5d13931c590169cf30bd7004e23f2df1b..57ed9e87b7fca6c899d4c23d709a97dabce28106 100644 (file)
--- a/show-index.c
+++ b/show-index.c
#include "cache.h"
+#include "pack.h"
int main(int argc, char **argv)
{
int i;
unsigned nr;
- unsigned int entry[6];
+ unsigned int version;
static unsigned int top_index[256];
- if (fread(top_index, sizeof(top_index), 1, stdin) != 1)
- die("unable to read index");
+ if (fread(top_index, 2 * 4, 1, stdin) != 1)
+ die("unable to read header");
+ if (top_index[0] == htonl(PACK_IDX_SIGNATURE)) {
+ version = ntohl(top_index[1]);
+ if (version < 2 || version > 2)
+ die("unknown index version");
+ if (fread(top_index, 256 * 4, 1, stdin) != 1)
+ die("unable to read index");
+ } else {
+ version = 1;
+ if (fread(&top_index[2], 254 * 4, 1, stdin) != 1)
+ die("unable to read index");
+ }
nr = 0;
for (i = 0; i < 256; i++) {
unsigned n = ntohl(top_index[i]);
die("corrupt index file");
nr = n;
}
- for (i = 0; i < nr; i++) {
- unsigned offset;
+ if (version == 1) {
+ for (i = 0; i < nr; i++) {
+ unsigned int offset, entry[6];
- if (fread(entry, 24, 1, stdin) != 1)
- die("unable to read entry %u/%u", i, nr);
- offset = ntohl(entry[0]);
- printf("%u %s\n", offset, sha1_to_hex((void *)(entry+1)));
+ if (fread(entry, 4 + 20, 1, stdin) != 1)
+ die("unable to read entry %u/%u", i, nr);
+ offset = ntohl(entry[0]);
+ printf("%u %s\n", offset, sha1_to_hex((void *)(entry+1)));
+ }
+ } else {
+ unsigned off64_nr = 0;
+ struct {
+ unsigned char sha1[20];
+ uint32_t crc;
+ uint32_t off;
+ } *entries = xmalloc(nr * sizeof(entries[0]));
+ for (i = 0; i < nr; i++)
+ if (fread(entries[i].sha1, 20, 1, stdin) != 1)
+ die("unable to read sha1 %u/%u", i, nr);
+ for (i = 0; i < nr; i++)
+ if (fread(&entries[i].crc, 4, 1, stdin) != 1)
+ die("unable to read crc %u/%u", i, nr);
+ for (i = 0; i < nr; i++)
+ if (fread(&entries[i].off, 4, 1, stdin) != 1)
+ die("unable to read 32b offset %u/%u", i, nr);
+ for (i = 0; i < nr; i++) {
+ uint64_t offset;
+ uint32_t off = ntohl(entries[i].off);
+ if (!(off & 0x80000000)) {
+ offset = off;
+ } else {
+ uint32_t off64[2];
+ if ((off & 0x7fffffff) != off64_nr)
+ die("inconsistent 64b offset index");
+ if (fread(off64, 8, 1, stdin) != 1)
+ die("unable to read 64b offset %u", off64_nr);
+ offset = (((uint64_t)ntohl(off64[0])) << 32) |
+ ntohl(off64[1]);
+ off64_nr++;
+ }
+ printf("%llu %s (%08x)\n", (unsigned long long) offset,
+ sha1_to_hex(entries[i].sha1),
+ ntohl(entries[i].crc));
+ }
+ free(entries);
}
return 0;
}
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index 723b29ad17f778f9f9a96682dee1793191b88667..fe1dfd08a02e7a8c9c26542e934ccd6fc4f16f5c 100755 (executable)
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
. ./test-lib.sh
+q_to_nul () {
+ tr Q '\0'
+}
+
append_cr () {
sed -e 's/$/Q/' | tr Q '\015'
}
for w in Hello world how are you; do echo $w; done >one &&
mkdir dir &&
for w in I am very very fine thank you; do echo $w; done >dir/two &&
+ for w in Oh here is NULQin text here; do echo $w; done | q_to_nul >three &&
git add . &&
git commit -m initial &&
one=`git rev-parse HEAD:one` &&
dir=`git rev-parse HEAD:dir` &&
two=`git rev-parse HEAD:dir/two` &&
+ three=`git rev-parse HEAD:three` &&
for w in Some extra lines here; do echo $w; done >>one &&
git diff >patch.file &&
test_expect_success 'update with autocrlf=input' '
- rm -f tmp one dir/two &&
+ rm -f tmp one dir/two three &&
git read-tree --reset -u HEAD &&
git repo-config core.autocrlf input &&
test_expect_success 'update with autocrlf=true' '
- rm -f tmp one dir/two &&
+ rm -f tmp one dir/two three &&
git read-tree --reset -u HEAD &&
git repo-config core.autocrlf true &&
test_expect_success 'checkout with autocrlf=true' '
- rm -f tmp one dir/two &&
+ rm -f tmp one dir/two three &&
git repo-config core.autocrlf true &&
git read-tree --reset -u HEAD &&
test_expect_success 'checkout with autocrlf=input' '
- rm -f tmp one dir/two &&
+ rm -f tmp one dir/two three &&
git repo-config core.autocrlf input &&
git read-tree --reset -u HEAD &&
test_expect_success 'apply patch (autocrlf=input)' '
- rm -f tmp one dir/two &&
+ rm -f tmp one dir/two three &&
git repo-config core.autocrlf input &&
git read-tree --reset -u HEAD &&
test_expect_success 'apply patch --cached (autocrlf=input)' '
- rm -f tmp one dir/two &&
+ rm -f tmp one dir/two three &&
git repo-config core.autocrlf input &&
git read-tree --reset -u HEAD &&
test_expect_success 'apply patch --index (autocrlf=input)' '
- rm -f tmp one dir/two &&
+ rm -f tmp one dir/two three &&
git repo-config core.autocrlf input &&
git read-tree --reset -u HEAD &&
test_expect_success 'apply patch (autocrlf=true)' '
- rm -f tmp one dir/two &&
+ rm -f tmp one dir/two three &&
git repo-config core.autocrlf true &&
git read-tree --reset -u HEAD &&
test_expect_success 'apply patch --cached (autocrlf=true)' '
- rm -f tmp one dir/two &&
+ rm -f tmp one dir/two three &&
git repo-config core.autocrlf true &&
git read-tree --reset -u HEAD &&
test_expect_success 'apply patch --index (autocrlf=true)' '
- rm -f tmp one dir/two &&
+ rm -f tmp one dir/two three &&
git repo-config core.autocrlf true &&
git read-tree --reset -u HEAD &&
}
'
+test_expect_success '.gitattributes says two is binary' '
+
+ rm -f tmp one dir/two three &&
+ echo "two -crlf" >.gitattributes &&
+ git repo-config core.autocrlf true &&
+ git read-tree --reset -u HEAD &&
+
+ if remove_cr dir/two >/dev/null
+ then
+ echo "Huh?"
+ false
+ else
+ : happy
+ fi &&
+
+ if remove_cr one >/dev/null
+ then
+ : happy
+ else
+ echo "Huh?"
+ false
+ fi &&
+
+ if remove_cr three >/dev/null
+ then
+ echo "Huh?"
+ false
+ else
+ : happy
+ fi
+'
+
+test_expect_success '.gitattributes says two is input' '
+
+ rm -f tmp one dir/two three &&
+ echo "two crlf=input" >.gitattributes &&
+ git read-tree --reset -u HEAD &&
+
+ if remove_cr dir/two >/dev/null
+ then
+ echo "Huh?"
+ false
+ else
+ : happy
+ fi
+'
+
+test_expect_success '.gitattributes says two and three are text' '
+
+ rm -f tmp one dir/two three &&
+ echo "t* crlf" >.gitattributes &&
+ git read-tree --reset -u HEAD &&
+
+ if remove_cr dir/two >/dev/null
+ then
+ : happy
+ else
+ echo "Huh?"
+ false
+ fi &&
+
+ if remove_cr three >/dev/null
+ then
+ : happy
+ else
+ echo "Huh?"
+ false
+ fi
+'
+
test_done
diff --git a/t/t3040-subprojects-basic.sh b/t/t3040-subprojects-basic.sh
--- /dev/null
@@ -0,0 +1,85 @@
+#!/bin/sh
+
+test_description='Basic subproject functionality'
+. ./test-lib.sh
+
+test_expect_success 'Super project creation' \
+ ': >Makefile &&
+ git add Makefile &&
+ git commit -m "Superproject created"'
+
+
+cat >expected <<EOF
+:000000 160000 00000... A sub1
+:000000 160000 00000... A sub2
+EOF
+test_expect_success 'create subprojects' \
+ 'mkdir sub1 &&
+ ( cd sub1 && git init && : >Makefile && git add * &&
+ git commit -q -m "subproject 1" ) &&
+ mkdir sub2 &&
+ ( cd sub2 && git init && : >Makefile && git add * &&
+ git commit -q -m "subproject 2" ) &&
+ git update-index --add sub1 &&
+ git add sub2 &&
+ git commit -q -m "subprojects added" &&
+ git diff-tree --abbrev=5 HEAD^ HEAD |cut -d" " -f-3,5- >current &&
+ git diff expected current'
+
+git branch save HEAD
+
+test_expect_success 'check if fsck ignores the subprojects' \
+ 'git fsck --full'
+
+test_expect_success 'check if commit in a subproject detected' \
+ '( cd sub1 &&
+ echo "all:" >>Makefile &&
+ echo " true" >>Makefile &&
+ git commit -q -a -m "make all" ) && {
+ git diff-files --exit-code
+ test $? = 1
+ }'
+
+test_expect_success 'check if a changed subproject HEAD can be committed' \
+ 'git commit -q -a -m "sub1 changed" && {
+ git diff-tree --exit-code HEAD^ HEAD
+ test $? = 1
+ }'
+
+test_expect_success 'check if diff-index works for subproject elements' \
+ 'git diff-index --exit-code --cached save -- sub1
+ test $? = 1'
+
+test_expect_success 'check if diff-tree works for subproject elements' \
+ 'git diff-tree --exit-code HEAD^ HEAD -- sub1
+ test $? = 1'
+
+test_expect_success 'check if git diff works for subproject elements' \
+ 'git diff --exit-code HEAD^ HEAD
+ test $? = 1'
+
+test_expect_success 'check if clone works' \
+ 'git ls-files -s >expected &&
+ git clone -l -s . cloned &&
+ ( cd cloned && git ls-files -s ) >current &&
+ git diff expected current'
+
+test_expect_success 'removing and adding subproject' \
+ 'git update-index --force-remove -- sub2 &&
+ mv sub2 sub3 &&
+ git add sub3 &&
+ git commit -q -m "renaming a subproject" && {
+ git diff -M --name-status --exit-code HEAD^ HEAD
+ test $? = 1
+ }'
+
+# the index must contain the object name the HEAD of the
+# subproject sub1 was at the point "save"
+test_expect_success 'checkout in superproject' \
+ 'git checkout save &&
+ git diff-index --exit-code --raw --cached save -- sub1'
+
+# just interesting what happened...
+# git diff --name-status -M save master
+
+test_done
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index 08e035220cfd5c2da0798eba84c779981f185b4a..ad8cc7d4ae88e2066d2f51b6a6a5a192780d5e9a 100755 (executable)
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
git-ls-files --error-unmatch d.ig/d.if d.ig/d.ig
'
+mkdir 1 1/2 1/3
+touch 1/2/a 1/3/b 1/2/c
+test_expect_success 'check correct prefix detection' '
+ git add 1/2/a 1/3/b 1/2/c
+'
+
test_done
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
--- /dev/null
+++ b/t/t4020-diff-external.sh
@@ -0,0 +1,97 @@
+#!/bin/sh
+
+test_description='external diff interface test'
+
+. ./test-lib.sh
+
+_z40=0000000000000000000000000000000000000000
+
+test_expect_success setup '
+
+ test_tick &&
+ echo initial >file &&
+ git add file &&
+ git commit -m initial &&
+
+ test_tick &&
+ echo second >file &&
+ git add file &&
+ git commit -m second &&
+
+ test_tick &&
+ echo third >file
+'
+
+test_expect_success 'GIT_EXTERNAL_DIFF environment' '
+
+ GIT_EXTERNAL_DIFF=echo git diff | {
+ read path oldfile oldhex oldmode newfile newhex newmode &&
+ test "z$path" = zfile &&
+ test "z$oldmode" = z100644 &&
+ test "z$newhex" = "z$_z40" &&
+ test "z$newmode" = z100644 &&
+ oh=$(git rev-parse --verify HEAD:file) &&
+ test "z$oh" = "z$oldhex"
+ }
+
+'
+
+test_expect_success 'GIT_EXTERNAL_DIFF environment should apply only to diff' '
+
+ GIT_EXTERNAL_DIFF=echo git log -p -1 HEAD |
+ grep "^diff --git a/file b/file"
+
+'
+
+test_expect_success 'diff attribute' '
+
+ git config diff.parrot.command echo &&
+
+ echo >.gitattributes "file diff=parrot" &&
+
+ git diff | {
+ read path oldfile oldhex oldmode newfile newhex newmode &&
+ test "z$path" = zfile &&
+ test "z$oldmode" = z100644 &&
+ test "z$newhex" = "z$_z40" &&
+ test "z$newmode" = z100644 &&
+ oh=$(git rev-parse --verify HEAD:file) &&
+ test "z$oh" = "z$oldhex"
+ }
+
+'
+
+test_expect_success 'diff attribute should apply only to diff' '
+
+ git log -p -1 HEAD |
+ grep "^diff --git a/file b/file"
+
+'
+
+test_expect_success 'diff attribute' '
+
+ git config --unset diff.parrot.command &&
+ git config diff.color.command echo &&
+
+ echo >.gitattributes "file diff=color" &&
+
+ git diff | {
+ read path oldfile oldhex oldmode newfile newhex newmode &&
+ test "z$path" = zfile &&
+ test "z$oldmode" = z100644 &&
+ test "z$newhex" = "z$_z40" &&
+ test "z$newmode" = z100644 &&
+ oh=$(git rev-parse --verify HEAD:file) &&
+ test "z$oh" = "z$oldhex"
+ }
+
+'
+
+test_expect_success 'diff attribute should apply only to diff' '
+
+ git log -p -1 HEAD |
+ grep "^diff --git a/file b/file"
+
+'
+
+test_done
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
index c27e39cb6f85a68af29fae6849f2811eaee4c44a..a48733cee03d4f4728ad12304b718f9a412c0f71 100755 (executable)
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
git update-ref HEAD $commit
echo 2 > a1
-git commit -m "This is a very, very long first line for the commit message to see if it is wrapped correctly" a1
+git commit --quiet -m "This is a very, very long first line for the commit message to see if it is wrapped correctly" a1
# test if the wrapping is still valid when replacing all i's by treble clefs.
echo 3 > a1
-git commit -m "$(echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" | sed "s/i/1234/g" | tr 1234 '\360\235\204\236')" a1
+git commit --quiet -m "$(echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" | sed "s/i/1234/g" | tr 1234 '\360\235\204\236')" a1
# now fsck up the utf8
git repo-config i18n.commitencoding non-utf-8
echo 4 > a1
-git commit -m "$(echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" | sed "s/i/1234/g" | tr 1234 '\370\235\204\236')" a1
+git commit --quiet -m "$(echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" | sed "s/i/1234/g" | tr 1234 '\370\235\204\236')" a1
echo 5 > a1
-git commit -m "a 12 34 56 78" a1
+git commit --quiet -m "a 12 34 56 78" a1
git shortlog -w HEAD > out
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 083095f7f3a6720836f135835091e6d27d18617a..f336769836f794c390d6e0ddd19f98da5c7b6e40 100755 (executable)
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
'use packed deltified (REF_DELTA) objects' \
'GIT_OBJECT_DIRECTORY=.git2/objects &&
export GIT_OBJECT_DIRECTORY &&
- rm .git2/objects/pack/test-* &&
+ rm -f .git2/objects/pack/test-* &&
cp test-2-${packname_2}.pack test-2-${packname_2}.idx .git2/objects/pack && {
git-diff-tree --root -p $commit &&
while read object
'use packed deltified (OFS_DELTA) objects' \
'GIT_OBJECT_DIRECTORY=.git2/objects &&
export GIT_OBJECT_DIRECTORY &&
- rm .git2/objects/pack/test-* &&
+ rm -f .git2/objects/pack/test-* &&
cp test-3-${packname_3}.pack test-3-${packname_3}.idx .git2/objects/pack && {
git-diff-tree --root -p $commit &&
while read object
test_expect_success \
'corrupt a pack and see if verify catches' \
- 'cp test-1-${packname_1}.idx test-3.idx &&
- cp test-2-${packname_2}.pack test-3.pack &&
+ 'cat test-1-${packname_1}.idx >test-3.idx &&
+ cat test-2-${packname_2}.pack >test-3.pack &&
if git-verify-pack test-3.idx
then false
else :;
fi &&
: PACK_SIGNATURE &&
- cp test-1-${packname_1}.pack test-3.pack &&
+ cat test-1-${packname_1}.pack >test-3.pack &&
dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=2 &&
if git-verify-pack test-3.idx
then false
fi &&
: PACK_VERSION &&
- cp test-1-${packname_1}.pack test-3.pack &&
+ cat test-1-${packname_1}.pack >test-3.pack &&
dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=7 &&
if git-verify-pack test-3.idx
then false
fi &&
: TYPE/SIZE byte of the first packed object data &&
- cp test-1-${packname_1}.pack test-3.pack &&
+ cat test-1-${packname_1}.pack >test-3.pack &&
dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=12 &&
if git-verify-pack test-3.idx
then false
: sum of the index file itself &&
l=`wc -c <test-3.idx` &&
l=`expr $l - 20` &&
- cp test-1-${packname_1}.pack test-3.pack &&
+ cat test-1-${packname_1}.pack >test-3.pack &&
dd if=/dev/zero of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
if git-verify-pack test-3.pack
then false
test_expect_success \
'build pack index for an existing pack' \
- 'cp test-1-${packname_1}.pack test-3.pack &&
+ 'cat test-1-${packname_1}.pack >test-3.pack &&
git-index-pack -o tmp.idx test-3.pack &&
cmp tmp.idx test-1-${packname_1}.idx &&
git-index-pack test-3.pack &&
cmp test-3.idx test-1-${packname_1}.idx &&
- cp test-2-${packname_2}.pack test-3.pack &&
+ cat test-2-${packname_2}.pack >test-3.pack &&
git-index-pack -o tmp.idx test-2-${packname_2}.pack &&
cmp tmp.idx test-2-${packname_2}.idx &&
git-index-pack test-3.pack &&
cmp test-3.idx test-2-${packname_2}.idx &&
- cp test-3-${packname_3}.pack test-3.pack &&
+ cat test-3-${packname_3}.pack >test-3.pack &&
git-index-pack -o tmp.idx test-3-${packname_3}.pack &&
cmp tmp.idx test-3-${packname_3}.idx &&
index a6dbb04a86c06cabb22810822dd0f079c9268dd7..fce77f1255378b715c23be5978fcc13e56ba263d 100755 (executable)
for i in a b c
do
echo $i >$i &&
- dd if=/dev/urandom bs=32k count=1 >>$i &&
+ test-genrandom "$i" 32768 >>$i &&
git-update-index --add $i || return 1
done &&
echo d >d && cat c >>d && git-update-index --add d &&
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
--- /dev/null
+++ b/t/t5302-pack-index.sh
@@ -0,0 +1,146 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Nicolas Pitre
+#
+
+test_description='pack index with 64-bit offsets and object CRC'
+. ./test-lib.sh
+
+test_expect_success \
+ 'setup' \
+ 'rm -rf .git
+ git-init &&
+ for i in `seq -w 100`
+ do
+ echo $i >file_$i &&
+ test-genrandom "$i" 8192 >>file_$i &&
+ git-update-index --add file_$i || return 1
+ done &&
+ { echo 101 && test-genrandom 100 8192; } >file_101 &&
+ git-update-index --add file_101 &&
+ tree=`git-write-tree` &&
+ commit=`git-commit-tree $tree </dev/null` && {
+ echo $tree &&
+ git-ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
+ } >obj-list &&
+ git-update-ref HEAD $commit'
+
+test_expect_success \
+ 'pack-objects with index version 1' \
+ 'pack1=$(git-pack-objects --index-version=1 test-1 <obj-list) &&
+ git-verify-pack -v "test-1-${pack1}.pack"'
+
+test_expect_success \
+ 'pack-objects with index version 2' \
+ 'pack2=$(git-pack-objects --index-version=2 test-2 <obj-list) &&
+ git-verify-pack -v "test-2-${pack2}.pack"'
+
+test_expect_success \
+ 'both packs should be identical' \
+ 'cmp "test-1-${pack1}.pack" "test-2-${pack2}.pack"'
+
+test_expect_failure \
+ 'index v1 and index v2 should be different' \
+ 'cmp "test-1-${pack1}.idx" "test-2-${pack2}.idx"'
+
+test_expect_success \
+ 'index-pack with index version 1' \
+ 'git-index-pack --index-version=1 -o 1.idx "test-1-${pack1}.pack"'
+
+test_expect_success \
+ 'index-pack with index version 2' \
+ 'git-index-pack --index-version=2 -o 2.idx "test-1-${pack1}.pack"'
+
+test_expect_success \
+ 'index-pack results should match pack-objects ones' \
+ 'cmp "test-1-${pack1}.idx" "1.idx" &&
+ cmp "test-2-${pack2}.idx" "2.idx"'
+
+test_expect_success \
+ 'index v2: force some 64-bit offsets with pack-objects' \
+ 'pack3=$(git-pack-objects --index-version=2,0x40000 test-3 <obj-list) &&
+ git-verify-pack -v "test-3-${pack3}.pack"'
+
+test_expect_failure \
+ '64-bit offsets: should be different from previous index v2 results' \
+ 'cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"'
+
+test_expect_success \
+ 'index v2: force some 64-bit offsets with index-pack' \
+ 'git-index-pack --index-version=2,0x40000 -o 3.idx "test-1-${pack1}.pack"'
+
+test_expect_success \
+ '64-bit offsets: index-pack result should match pack-objects one' \
+ 'cmp "test-3-${pack3}.idx" "3.idx"'
+
+test_expect_success \
+ '[index v1] 1) stream pack to repository' \
+ 'git-index-pack --index-version=1 --stdin < "test-1-${pack1}.pack" &&
+ git-prune-packed &&
+ git-count-objects | ( read nr rest && test "$nr" -eq 1 ) &&
+ cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" &&
+ cmp "test-1-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"'
+
+test_expect_success \
+ '[index v1] 2) create a stealth corruption in a delta base reference' \
+ '# this test assumes a delta smaller than 16 bytes at the end of the pack
+ git-show-index <1.idx | sort -n | tail -n 1 | (
+ read delta_offs delta_sha1 &&
+ git-cat-file blob "$delta_sha1" > blob_1 &&
+ chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
+ dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($delta_offs + 1)) \
+ if=".git/objects/pack/pack-${pack1}.idx" skip=$((256 * 4 + 4)) \
+ bs=1 count=20 conv=notrunc &&
+ git-cat-file blob "$delta_sha1" > blob_2 )'
+
+test_expect_failure \
+ '[index v1] 3) corrupted delta happily returned wrong data' \
+ 'cmp blob_1 blob_2'
+
+test_expect_failure \
+ '[index v1] 4) confirm that the pack is actually corrupted' \
+ 'git-fsck --full $commit'
+
+test_expect_success \
+ '[index v1] 5) pack-objects happily reuses corrupted data' \
+ 'pack4=$(git-pack-objects test-4 <obj-list) &&
+ test -f "test-4-${pack1}.pack"'
+
+test_expect_failure \
+ '[index v1] 6) newly created pack is BAD !' \
+ 'git-verify-pack -v "test-4-${pack1}.pack"'
+
+test_expect_success \
+ '[index v2] 1) stream pack to repository' \
+ 'rm -f .git/objects/pack/* &&
+ git-index-pack --index-version=2,0x40000 --stdin < "test-1-${pack1}.pack" &&
+ git-prune-packed &&
+ git-count-objects | ( read nr rest && test "$nr" -eq 1 ) &&
+ cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" &&
+ cmp "test-3-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"'
+
+test_expect_success \
+ '[index v2] 2) create a stealth corruption in a delta base reference' \
+ '# this test assumes a delta smaller than 16 bytes at the end of the pack
+ git-show-index <1.idx | sort -n | tail -n 1 | (
+ read delta_offs delta_sha1 delta_crc &&
+ git-cat-file blob "$delta_sha1" > blob_3 &&
+ chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
+ dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($delta_offs + 1)) \
+ if=".git/objects/pack/pack-${pack1}.idx" skip=$((8 + 256 * 4)) \
+ bs=1 count=20 conv=notrunc &&
+ git-cat-file blob "$delta_sha1" > blob_4 )'
+
+test_expect_failure \
+ '[index v2] 3) corrupted delta happily returned wrong data' \
+ 'cmp blob_3 blob_4'
+
+test_expect_failure \
+ '[index v2] 4) confirm that the pack is actually corrupted' \
+ 'git-fsck --full $commit'
+
+test_expect_failure \
+ '[index v2] 5) pack-objects refuses to reuse corrupted data' \
+ 'git-pack-objects test-5 <obj-list'
+
+test_done
diff --git a/t/t5502-quickfetch.sh b/t/t5502-quickfetch.sh
--- /dev/null
+++ b/t/t5502-quickfetch.sh
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+test_description='test quickfetch from local'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ test_tick &&
+ echo ichi >file &&
+ git add file &&
+ git commit -m initial &&
+
+ cnt=$( (
+ git count-objects | sed -e "s/ *objects,.*//"
+ ) ) &&
+ test $cnt -eq 3
+'
+
+test_expect_success 'clone without alternate' '
+
+ (
+ mkdir cloned &&
+ cd cloned &&
+ git init-db &&
+ git remote add -f origin ..
+ ) &&
+ cnt=$( (
+ cd cloned &&
+ git count-objects | sed -e "s/ *objects,.*//"
+ ) ) &&
+ test $cnt -eq 3
+'
+
+test_expect_success 'further commits in the original' '
+
+ test_tick &&
+ echo ni >file &&
+ git commit -a -m second &&
+
+ cnt=$( (
+ git count-objects | sed -e "s/ *objects,.*//"
+ ) ) &&
+ test $cnt -eq 6
+'
+
+test_expect_success 'copy commit and tree but not blob by hand' '
+
+ git rev-list --objects HEAD |
+ git pack-objects --stdout |
+ (
+ cd cloned &&
+ git unpack-objects
+ ) &&
+
+ cnt=$( (
+ cd cloned &&
+ git count-objects | sed -e "s/ *objects,.*//"
+ ) ) &&
+ test $cnt -eq 6
+
+ blob=$(git rev-parse HEAD:file | sed -e "s|..|&/|") &&
+ test -f "cloned/.git/objects/$blob" &&
+ rm -f "cloned/.git/objects/$blob" &&
+
+ cnt=$( (
+ cd cloned &&
+ git count-objects | sed -e "s/ *objects,.*//"
+ ) ) &&
+ test $cnt -eq 5
+
+'
+
+test_expect_success 'quickfetch should not leave a corrupted repository' '
+
+ (
+ cd cloned &&
+ git fetch
+ ) &&
+
+ cnt=$( (
+ cd cloned &&
+ git count-objects | sed -e "s/ *objects,.*//"
+ ) ) &&
+ test $cnt -eq 6
+
+'
+
+test_done
diff --git a/t/t6026-merge-attr.sh b/t/t6026-merge-attr.sh
--- /dev/null
+++ b/t/t6026-merge-attr.sh
@@ -0,0 +1,145 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Junio C Hamano
+#
+
+test_description='per path merge controlled by merge attribute'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ for f in text binary union
+ do
+ echo Initial >$f && git add $f || break
+ done &&
+ test_tick &&
+ git commit -m Initial &&
+
+ git branch side &&
+ for f in text binary union
+ do
+ echo Master >>$f && git add $f || break
+ done &&
+ test_tick &&
+ git commit -m Master &&
+
+ git checkout side &&
+ for f in text binary union
+ do
+ echo Side >>$f && git add $f || break
+ done &&
+ test_tick &&
+ git commit -m Side &&
+
+ git tag anchor
+'
+
+test_expect_success merge '
+
+ {
+ echo "binary -merge"
+ echo "union merge=union"
+ } >.gitattributes &&
+
+ if git merge master
+ then
+ echo Gaah, should have conflicted
+ false
+ else
+ echo Ok, conflicted.
+ fi
+'
+
+test_expect_success 'check merge result in index' '
+
+ git ls-files -u | grep binary &&
+ git ls-files -u | grep text &&
+ ! (git ls-files -u | grep union)
+
+'
+
+test_expect_success 'check merge result in working tree' '
+
+ git cat-file -p HEAD:binary >binary-orig &&
+ grep "<<<<<<<" text &&
+ cmp binary-orig binary &&
+ ! grep "<<<<<<<" union &&
+ grep Master union &&
+ grep Side union
+
+'
+
+cat >./custom-merge <<\EOF
+#!/bin/sh
+
+orig="$1" ours="$2" theirs="$3" exit="$4"
+(
+ echo "orig is $orig"
+ echo "ours is $ours"
+ echo "theirs is $theirs"
+ echo "=== orig ==="
+ cat "$orig"
+ echo "=== ours ==="
+ cat "$ours"
+ echo "=== theirs ==="
+ cat "$theirs"
+) >"$ours+"
+cat "$ours+" >"$ours"
+rm -f "$ours+"
+exit "$exit"
+EOF
+chmod +x ./custom-merge
+
+test_expect_success 'custom merge backend' '
+
+ echo "* merge=union" >.gitattributes &&
+ echo "text merge=custom" >>.gitattributes &&
+
+ git reset --hard anchor &&
+ git config --replace-all \
+ merge.custom.driver "./custom-merge %O %A %B 0" &&
+ git config --replace-all \
+ merge.custom.name "custom merge driver for testing" &&
+
+ git merge master &&
+
+ cmp binary union &&
+ sed -e 1,3d text >check-1 &&
+ o=$(git-unpack-file master^:text) &&
+ a=$(git-unpack-file side^:text) &&
+ b=$(git-unpack-file master:text) &&
+ sh -c "./custom-merge $o $a $b 0" &&
+ sed -e 1,3d $a >check-2 &&
+ cmp check-1 check-2 &&
+ rm -f $o $a $b
+'
+
+test_expect_success 'custom merge backend' '
+
+ git reset --hard anchor &&
+ git config --replace-all \
+ merge.custom.driver "./custom-merge %O %A %B 1" &&
+ git config --replace-all \
+ merge.custom.name "custom merge driver for testing" &&
+
+ if git merge master
+ then
+ echo "Eh? should have conflicted"
+ false
+ else
+ echo "Ok, conflicted"
+ fi &&
+
+ cmp binary union &&
+ sed -e 1,3d text >check-1 &&
+ o=$(git-unpack-file master^:text) &&
+ a=$(git-unpack-file anchor:text) &&
+ b=$(git-unpack-file master:text) &&
+ sh -c "./custom-merge $o $a $b 0" &&
+ sed -e 1,3d $a >check-2 &&
+ cmp check-1 check-2 &&
+ rm -f $o $a $b
+'
+
+test_done
index 13e937923658868c16fee6aefc52c8f180cafdfb..30f6ade13f088df1ce6c7e3649c2afb343649d65 100755 (executable)
MSG="Create file <$_file> with <$_line> inside."
fi
- git-commit -m "$MSG" $_file
+ test_tick
+ git-commit --quiet -m "$MSG" $_file
}
HASH1=
+HASH2=
HASH3=
HASH4=
-test_expect_success \
- 'set up basic repo with 1 file (hello) and 4 commits' \
- 'add_line_into_file "1: Hello World" hello &&
+test_expect_success 'set up basic repo with 1 file (hello) and 4 commits' '
+ add_line_into_file "1: Hello World" hello &&
+ HASH1=$(git rev-parse --verify HEAD) &&
add_line_into_file "2: A new day for git" hello &&
+ HASH2=$(git rev-parse --verify HEAD) &&
add_line_into_file "3: Another new day for git" hello &&
+ HASH3=$(git rev-parse --verify HEAD) &&
add_line_into_file "4: Ciao for now" hello &&
- HASH1=$(git rev-list HEAD | tail -1) &&
- HASH3=$(git rev-list HEAD | head -2 | tail -1) &&
- HASH4=$(git rev-list HEAD | head -1)'
+ HASH4=$(git rev-parse --verify HEAD)
+'
test_expect_success 'bisect starts with only one bad' '
git bisect reset &&
diff --git a/t/test-lib.sh b/t/test-lib.sh
index c0754747fbc250274f069901703159ffd23faf86..f2c6bd3b01d39b8cc71bc624e0eaddc0681ae25e 100644 (file)
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
export EDITOR VISUAL
+# Protect ourselves from common misconfiguration to export
+# CDPATH into the environment
+unset CDPATH
+
case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in
1|2|true)
echo "* warning: Some tests will not work if GIT_TRACE" \
index 56a49f4fe1f705ee70bc5318a504c35d1bce963e..330d287924765c95dac428f040b104930c316b81 100644 (file)
--- a/tag.c
+++ b/tag.c
struct tag *lookup_tag(const unsigned char *sha1)
{
- struct object *obj = lookup_object(sha1);
- if (!obj) {
- struct tag *ret = alloc_tag_node();
- created_object(sha1, &ret->object);
- ret->object.type = OBJ_TAG;
- return ret;
- }
+ struct object *obj = lookup_object(sha1);
+ if (!obj)
+ return create_object(sha1, OBJ_TAG, alloc_tag_node());
if (!obj->type)
obj->type = OBJ_TAG;
if (obj->type != OBJ_TAG) {
diff --git a/test-genrandom.c b/test-genrandom.c
--- /dev/null
+++ b/test-genrandom.c
@@ -0,0 +1,34 @@
+/*
+ * Simple random data generator used to create reproducible test files.
+ * This is inspired from POSIX.1-2001 implementation example for rand().
+ * Copyright (C) 2007 by Nicolas Pitre, licensed under the GPL version 2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+ unsigned long count, next = 0;
+ unsigned char *c;
+
+ if (argc < 2 || argc > 3) {
+ fprintf( stderr, "Usage: %s <seed_string> [<size>]", argv[0]);
+ return 1;
+ }
+
+ c = (unsigned char *) argv[1];
+ do {
+ next = next * 11 + *c;
+ } while (*c++);
+
+ count = (argc == 3) ? strtoul(argv[2], NULL, 0) : -1L;
+
+ while (count--) {
+ next = next * 1103515245 + 12345;
+ if (putchar((next >> 16) & 0xff) == EOF)
+ return -1;
+ }
+
+ return 0;
+}
index d188c0fbaee110a17ca7a0d16dcc979091f44ded..e4a39aa3c36964c9034d2393a8b9cb483d1543da 100644 (file)
--- a/tree.c
+++ b/tree.c
struct tree *lookup_tree(const unsigned char *sha1)
{
struct object *obj = lookup_object(sha1);
- if (!obj) {
- struct tree *ret = alloc_tree_node();
- created_object(sha1, &ret->object);
- ret->object.type = OBJ_TREE;
- return ret;
- }
+ if (!obj)
+ return create_object(sha1, OBJ_TREE, alloc_tree_node());
if (!obj->type)
obj->type = OBJ_TREE;
if (obj->type != OBJ_TREE) {
return (struct tree *) obj;
}
+/*
+ * NOTE! Tree refs to external git repositories
+ * (ie gitlinks) do not count as real references.
+ *
+ * You don't have to have those repositories
+ * available at all, much less have the objects
+ * accessible from the current repository.
+ */
static void track_tree_refs(struct tree *item)
{
int n_refs = 0, i;
/* Count how many entries there are.. */
init_tree_desc(&desc, item->buffer, item->size);
- while (tree_entry(&desc, &entry))
+ while (tree_entry(&desc, &entry)) {
+ if (S_ISDIRLNK(entry.mode))
+ continue;
n_refs++;
+ }
/* Allocate object refs and walk it again.. */
i = 0;
while (tree_entry(&desc, &entry)) {
struct object *obj;
+ if (S_ISDIRLNK(entry.mode))
+ continue;
if (S_ISDIR(entry.mode))
obj = &lookup_tree(entry.sha1)->object;
else
diff --git a/unpack-trees.c b/unpack-trees.c
index 5139481358d0838a95db3fdb62e32221eb7343b1..675a9998dcbab528faf2cf34ab1a66f884a36300 100644 (file)
--- a/unpack-trees.c
+++ b/unpack-trees.c
#include "tree-walk.h"
#include "cache-tree.h"
#include "unpack-trees.h"
+#include "progress.h"
#define DBRT_DEBUG 1
}
}
-static volatile sig_atomic_t progress_update;
-
-static void progress_interval(int signum)
-{
- progress_update = 1;
-}
-
-static void setup_progress_signal(void)
-{
- struct sigaction sa;
- struct itimerval v;
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = progress_interval;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
- sigaction(SIGALRM, &sa, NULL);
-
- v.it_interval.tv_sec = 1;
- v.it_interval.tv_usec = 0;
- v.it_value = v.it_interval;
- setitimer(ITIMER_REAL, &v, NULL);
-}
-
static struct checkout state;
static void check_updates(struct cache_entry **src, int nr,
struct unpack_trees_options *o)
{
unsigned short mask = htons(CE_UPDATE);
- unsigned last_percent = 200, cnt = 0, total = 0;
+ unsigned cnt = 0, total = 0;
+ struct progress progress;
if (o->update && o->verbose_update) {
for (total = cnt = 0; cnt < nr; cnt++) {
total++;
}
- /* Don't bother doing this for very small updates */
- if (total < 250)
- total = 0;
-
- if (total) {
- fprintf(stderr, "Checking files out...\n");
- setup_progress_signal();
- progress_update = 1;
- }
+ start_progress_delay(&progress, "Checking %u files out...",
+ "", total, 50, 2);
cnt = 0;
}
while (nr--) {
struct cache_entry *ce = *src++;
- if (total) {
- if (!ce->ce_mode || ce->ce_flags & mask) {
- unsigned percent;
- cnt++;
- percent = (cnt * 100) / total;
- if (percent != last_percent ||
- progress_update) {
- fprintf(stderr, "%4u%% (%u/%u) done\r",
- percent, cnt, total);
- last_percent = percent;
- progress_update = 0;
- }
- }
- }
+ if (total)
+ if (!ce->ce_mode || ce->ce_flags & mask)
+ display_progress(&progress, ++cnt);
if (!ce->ce_mode) {
if (o->update)
unlink_entry(ce->name);
checkout_entry(ce, &state, NULL);
}
}
- if (total) {
- signal(SIGALRM, SIG_IGN);
- fputc('\n', stderr);
- }
+ if (total)
+ stop_progress(&progress);;
}
int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)