mirror of
https://github.com/git/git.git
synced 2026-03-13 18:33:25 +01:00
Merge commit '520d7e278cfd25057e883575060b7378dfab61dc'
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -16,6 +16,7 @@ git-blame
|
||||
git-branch
|
||||
git-bundle
|
||||
git-cat-file
|
||||
git-check-attr
|
||||
git-check-ref-format
|
||||
git-checkout
|
||||
git-checkout-index
|
||||
@@ -149,6 +150,7 @@ test-chmtime
|
||||
test-date
|
||||
test-delta
|
||||
test-dump-cache-tree
|
||||
test-genrandom
|
||||
test-match-trees
|
||||
common-cmds.h
|
||||
*.tar.gz
|
||||
|
||||
3
Documentation/.gitignore
vendored
3
Documentation/.gitignore
vendored
@@ -1,7 +1,6 @@
|
||||
*.xml
|
||||
*.html
|
||||
*.1
|
||||
*.7
|
||||
*.[1-8]
|
||||
*.made
|
||||
howto-index.txt
|
||||
doc.dep
|
||||
|
||||
@@ -2,9 +2,10 @@ MAN1_TXT= \
|
||||
$(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
|
||||
@@ -23,12 +24,14 @@ SP_ARTICLES = howto/revert-branch-rebase user-manual
|
||||
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=
|
||||
|
||||
@@ -53,15 +56,19 @@ all: html man
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@@ -99,7 +106,7 @@ cmd-list.made: cmd-list.perl $(MAN1_TXT)
|
||||
git.7 git.html: git.txt core-intro.txt
|
||||
|
||||
clean:
|
||||
rm -f *.xml *.xml+ *.html *.html+ *.1 *.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
|
||||
@@ -109,7 +116,7 @@ clean:
|
||||
sed -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' >$@+
|
||||
mv $@+ $@
|
||||
|
||||
%.1 %.7 : %.xml
|
||||
%.1 %.5 %.7 : %.xml
|
||||
xmlto -m callouts.xsl man $<
|
||||
|
||||
%.xml : %.txt
|
||||
|
||||
@@ -4,6 +4,38 @@ GIT v1.5.2 Release Notes (draft)
|
||||
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
|
||||
@@ -17,6 +49,10 @@ Updates since v1.5.1
|
||||
- "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
|
||||
@@ -41,6 +77,16 @@ Updates since v1.5.1
|
||||
- "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
|
||||
@@ -65,34 +111,11 @@ Updates since v1.5.1
|
||||
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
|
||||
@@ -110,10 +133,12 @@ The following are all in v1.5.1.x series, unless otherwise noted.
|
||||
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
|
||||
|
||||
@@ -84,6 +84,7 @@ git-bundle mainporcelain
|
||||
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
|
||||
|
||||
@@ -300,6 +300,10 @@ branch.<name>.merge::
|
||||
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`),
|
||||
@@ -525,6 +529,19 @@ merge.verbosity::
|
||||
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.
|
||||
|
||||
37
Documentation/git-check-attr.txt
Normal file
37
Documentation/git-check-attr.txt
Normal file
@@ -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
|
||||
|
||||
@@ -8,7 +8,7 @@ git-checkout - Checkout and switch to a branch
|
||||
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
|
||||
|
||||
@@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-clean' [-d] [-n] [-q] [-x | -X] [--] <paths>...
|
||||
'git-clean' [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -25,6 +25,10 @@ OPTIONS
|
||||
-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.
|
||||
|
||||
|
||||
@@ -68,6 +68,11 @@ OPTIONS
|
||||
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
|
||||
----
|
||||
|
||||
@@ -138,6 +138,11 @@ base-name::
|
||||
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
|
||||
------
|
||||
|
||||
@@ -67,6 +67,8 @@ message, or both. Leaves working tree as it was before "reset".
|
||||
<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::
|
||||
+
|
||||
|
||||
@@ -37,7 +37,11 @@ Documentation for older releases are available here:
|
||||
|
||||
* 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]
|
||||
|
||||
|
||||
311
Documentation/gitattributes.txt
Normal file
311
Documentation/gitattributes.txt
Normal file
@@ -0,0 +1,311 @@
|
||||
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.
|
||||
|
||||
When more than one glob pattern matches the path, a later line
|
||||
overrides an earlier line.
|
||||
|
||||
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 by "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
|
||||
15
Makefile
15
Makefile
@@ -283,7 +283,7 @@ LIB_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 \
|
||||
spawn-pipe.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 \
|
||||
@@ -306,7 +306,7 @@ LIB_OBJS = \
|
||||
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 \
|
||||
@@ -317,6 +317,7 @@ BUILTIN_OBJS = \
|
||||
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 \
|
||||
@@ -959,7 +960,7 @@ endif
|
||||
export NO_SYMLINKS
|
||||
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
|
||||
@@ -980,6 +981,9 @@ test-match-trees$X: test-match-trees.o $(GITLIBS)
|
||||
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
|
||||
|
||||
@@ -1052,9 +1056,10 @@ dist-doc:
|
||||
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 .
|
||||
@@ -1065,7 +1070,7 @@ dist-doc:
|
||||
|
||||
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
|
||||
|
||||
30
alloc.c
30
alloc.c
@@ -18,26 +18,38 @@
|
||||
|
||||
#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"
|
||||
|
||||
565
attr.c
Normal file
565
attr.c
Normal file
@@ -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;
|
||||
}
|
||||
34
attr.h
Normal file
34
attr.h
Normal file
@@ -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 */
|
||||
8
blob.c
8
blob.c
@@ -6,12 +6,8 @@ const char *blob_type = "blob";
|
||||
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) {
|
||||
|
||||
@@ -8,10 +8,15 @@
|
||||
#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)
|
||||
@@ -92,6 +97,44 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec)
|
||||
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")) {
|
||||
@@ -156,8 +199,20 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
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");
|
||||
@@ -207,6 +262,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
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))
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -83,17 +83,21 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
|
||||
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 */
|
||||
}
|
||||
|
||||
@@ -121,15 +125,17 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
|
||||
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;
|
||||
@@ -138,15 +144,15 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
|
||||
/* 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;
|
||||
|
||||
59
builtin-check-attr.c
Normal file
59
builtin-check-attr.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -115,7 +115,7 @@ int cmd_count_objects(int ac, const char **av, const char *prefix)
|
||||
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);
|
||||
|
||||
@@ -225,6 +225,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
|
||||
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.
|
||||
|
||||
@@ -436,10 +436,87 @@ static int expand_refs_wildcard(const char *ls_remote_result, int numrefs,
|
||||
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];
|
||||
@@ -447,6 +524,8 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
|
||||
verbose = 1;
|
||||
else if (!strcmp("-f", arg))
|
||||
force = 1;
|
||||
else if (!strcmp("-s", arg))
|
||||
sopt = 1;
|
||||
else
|
||||
break;
|
||||
argc--;
|
||||
@@ -491,6 +570,15 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
|
||||
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)
|
||||
|
||||
@@ -253,6 +253,7 @@ static int fsck_tree(struct tree *item)
|
||||
case S_IFREG | 0644:
|
||||
case S_IFLNK:
|
||||
case S_IFDIR:
|
||||
case S_IFDIRLNK:
|
||||
break;
|
||||
/*
|
||||
* This is nonstandard, but we had a few of these
|
||||
@@ -661,7 +662,7 @@ int cmd_fsck(int argc, char **argv, const char *prefix)
|
||||
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));
|
||||
}
|
||||
@@ -703,8 +704,14 @@ int cmd_fsck(int argc, char **argv, const char *prefix)
|
||||
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;
|
||||
|
||||
@@ -89,20 +89,38 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
|
||||
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);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "cache.h"
|
||||
#include "blob.h"
|
||||
#include "tree.h"
|
||||
#include "commit.h"
|
||||
#include "quote.h"
|
||||
#include "builtin.h"
|
||||
|
||||
@@ -59,7 +60,24 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
|
||||
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))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -113,6 +113,10 @@ static void show_object(struct object_array_entry *p)
|
||||
* 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),
|
||||
|
||||
@@ -7,13 +7,15 @@
|
||||
#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;
|
||||
|
||||
/*
|
||||
@@ -49,6 +51,10 @@ static void use(int bytes)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -88,17 +94,17 @@ static void *get_data(unsigned long size)
|
||||
|
||||
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));
|
||||
@@ -113,7 +119,7 @@ static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
|
||||
}
|
||||
|
||||
struct obj_info {
|
||||
unsigned long offset;
|
||||
off_t offset;
|
||||
unsigned char sha1[20];
|
||||
};
|
||||
|
||||
@@ -200,7 +206,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
|
||||
} else {
|
||||
unsigned base_found = 0;
|
||||
unsigned char *pack, c;
|
||||
unsigned long base_offset;
|
||||
off_t base_offset;
|
||||
unsigned lo, mid, hi;
|
||||
|
||||
pack = fill(1);
|
||||
@@ -209,7 +215,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
|
||||
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;
|
||||
@@ -259,7 +265,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
|
||||
free(base);
|
||||
}
|
||||
|
||||
static void unpack_one(unsigned nr, unsigned total)
|
||||
static void unpack_one(unsigned nr)
|
||||
{
|
||||
unsigned shift;
|
||||
unsigned char *pack, c;
|
||||
@@ -281,20 +287,7 @@ static void unpack_one(unsigned nr, unsigned total)
|
||||
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:
|
||||
@@ -318,6 +311,7 @@ static void unpack_one(unsigned nr, unsigned total)
|
||||
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);
|
||||
|
||||
@@ -325,12 +319,19 @@ static void unpack_all(void)
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
if (!quiet)
|
||||
start_progress(&progress, "Unpacking %u objects...", "", nr_objects);
|
||||
obj_list = xmalloc(nr_objects * sizeof(*obj_list));
|
||||
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");
|
||||
}
|
||||
@@ -399,7 +400,5 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
/* All done */
|
||||
if (!quiet)
|
||||
fprintf(stderr, "\n");
|
||||
return has_errors;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#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
|
||||
@@ -60,76 +61,151 @@ static int mark_valid(const char *path)
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
status = lstat(path, &st);
|
||||
/*
|
||||
* 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));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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))
|
||||
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 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);
|
||||
|
||||
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));
|
||||
}
|
||||
/*
|
||||
* 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);
|
||||
|
||||
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);
|
||||
len = strlen(path);
|
||||
if (S_ISDIR(st.st_mode))
|
||||
return process_directory(path, len, &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);
|
||||
|
||||
ent = (0 <= pos) ? active_cache[pos] : NULL;
|
||||
ce->ce_mode = ce_mode_from_stat(ent, st.st_mode);
|
||||
}
|
||||
|
||||
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 0;
|
||||
return process_file(path, len, &st);
|
||||
}
|
||||
|
||||
static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
|
||||
@@ -210,8 +286,8 @@ static void update_one(const char *path, const char *prefix, int prefix_length)
|
||||
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))
|
||||
|
||||
@@ -22,6 +22,7 @@ extern int cmd_branch(int argc, const char **argv, const char *prefix);
|
||||
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);
|
||||
|
||||
@@ -326,7 +326,7 @@ static int update_one(struct cache_tree *it,
|
||||
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)
|
||||
|
||||
50
cache.h
50
cache.h
@@ -24,6 +24,22 @@
|
||||
#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
|
||||
@@ -104,6 +120,8 @@ static inline unsigned int create_ce_mode(unsigned int mode)
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -151,6 +169,9 @@ enum object_type {
|
||||
#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);
|
||||
@@ -207,6 +228,7 @@ extern int refresh_cache(unsigned int flags);
|
||||
struct lock_file {
|
||||
struct lock_file *next;
|
||||
int fd;
|
||||
pid_t owner;
|
||||
char on_list;
|
||||
char filename[PATH_MAX];
|
||||
};
|
||||
@@ -377,11 +399,12 @@ struct pack_window {
|
||||
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];
|
||||
@@ -432,11 +455,11 @@ extern void pack_report(void);
|
||||
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 */
|
||||
@@ -477,14 +500,11 @@ int decode_85(char *dst, const char *line, int linelen);
|
||||
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 */
|
||||
@@ -494,8 +514,8 @@ extern void trace_printf(const char *format, ...);
|
||||
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);
|
||||
|
||||
@@ -943,6 +943,7 @@ void diff_tree_combined(const unsigned char *sha1,
|
||||
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;
|
||||
|
||||
8
commit.c
8
commit.c
@@ -98,12 +98,8 @@ struct commit *lookup_commit_reference(const unsigned char *sha1)
|
||||
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);
|
||||
|
||||
@@ -790,6 +790,7 @@ _git_config ()
|
||||
core.legacyHeaders
|
||||
core.packedGitWindowSize
|
||||
core.packedGitLimit
|
||||
clean.requireForce
|
||||
color.branch
|
||||
color.branch.current
|
||||
color.branch.local
|
||||
|
||||
191
convert.c
191
convert.c
@@ -1,4 +1,6 @@
|
||||
#include "cache.h"
|
||||
#include "attr.h"
|
||||
|
||||
/*
|
||||
* convert.c - convert a file when checking it out and checking it in.
|
||||
*
|
||||
@@ -8,6 +10,11 @@
|
||||
* 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;
|
||||
@@ -72,115 +79,171 @@ static int is_binary(unsigned long size, struct text_stat *stats)
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
14
csum-file.c
14
csum-file.c
@@ -49,6 +49,8 @@ int sha1close(struct sha1file *f, unsigned char *result, int update)
|
||||
|
||||
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;
|
||||
@@ -91,6 +93,7 @@ struct sha1file *sha1create(const char *fmt, ...)
|
||||
f->fd = fd;
|
||||
f->error = 0;
|
||||
f->offset = 0;
|
||||
f->do_crc = 0;
|
||||
SHA1_Init(&f->ctx);
|
||||
return f;
|
||||
}
|
||||
@@ -111,6 +114,7 @@ struct sha1file *sha1fd(int fd, const char *name)
|
||||
f->fd = fd;
|
||||
f->error = 0;
|
||||
f->offset = 0;
|
||||
f->do_crc = 0;
|
||||
SHA1_Init(&f->ctx);
|
||||
return f;
|
||||
}
|
||||
@@ -143,4 +147,14 @@ int sha1write_compressed(struct sha1file *f, void *in, unsigned int size)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ struct sha1file {
|
||||
unsigned int offset, namelen;
|
||||
SHA_CTX ctx;
|
||||
char name[PATH_MAX];
|
||||
int do_crc;
|
||||
uint32_t crc32;
|
||||
unsigned char buffer[8192];
|
||||
};
|
||||
|
||||
@@ -15,5 +17,7 @@ extern struct sha1file *sha1create(const char *fmt, ...) __attribute__((format (
|
||||
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
|
||||
|
||||
15
diff-lib.c
15
diff-lib.c
@@ -373,7 +373,7 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
|
||||
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];
|
||||
@@ -390,8 +390,7 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
|
||||
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;
|
||||
}
|
||||
@@ -440,15 +439,7 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
|
||||
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);
|
||||
|
||||
153
diff.c
153
diff.c
@@ -8,6 +8,7 @@
|
||||
#include "delta.h"
|
||||
#include "xdiff-interface.h"
|
||||
#include "color.h"
|
||||
#include "attr.h"
|
||||
#include "spawn-pipe.h"
|
||||
|
||||
#ifdef NO_FAST_WORKING_DIRECTORY
|
||||
@@ -52,6 +53,49 @@ static int parse_diff_color_slot(const char *var, int ofs)
|
||||
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
|
||||
@@ -78,11 +122,18 @@ int git_diff_ui_config(const char *var, const char *value)
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1052,13 +1103,39 @@ static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
|
||||
emit_binary_diff_body(two, one);
|
||||
}
|
||||
|
||||
#define FIRST_FEW_BYTES 8000
|
||||
static int mmfile_is_binary(mmfile_t *mf)
|
||||
static void setup_diff_attr_check(struct git_attr_check *check)
|
||||
{
|
||||
long sz = mf->size;
|
||||
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 file_is_binary(struct diff_filespec *one)
|
||||
{
|
||||
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,
|
||||
@@ -1115,7 +1192,7 @@ 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))
|
||||
@@ -1191,7 +1268,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
|
||||
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
|
||||
die("unable to read files to diff");
|
||||
|
||||
if (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;
|
||||
@@ -1229,7 +1306,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
|
||||
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
|
||||
die("unable to read files to diff");
|
||||
|
||||
if (mmfile_is_binary(&mf2))
|
||||
if (file_is_binary(two))
|
||||
return;
|
||||
else {
|
||||
/* Crazy xdl interfaces.. */
|
||||
@@ -1398,6 +1475,22 @@ static int populate_from_stdin(struct diff_filespec *s)
|
||||
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.
|
||||
@@ -1416,6 +1509,10 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
|
||||
|
||||
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;
|
||||
@@ -1462,9 +1559,9 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
|
||||
/*
|
||||
* 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;
|
||||
@@ -1695,6 +1792,30 @@ static void run_external_diff(const char *pgm,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -1704,6 +1825,14 @@ static void run_diff_cmd(const char *pgm,
|
||||
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);
|
||||
@@ -1800,8 +1929,8 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
|
||||
|
||||
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,
|
||||
@@ -2696,7 +2825,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));
|
||||
|
||||
1
diff.h
1
diff.h
@@ -59,6 +59,7 @@ struct diff_options {
|
||||
color_diff_words:1,
|
||||
has_changes:1,
|
||||
quiet:1,
|
||||
allow_external:1,
|
||||
exit_with_status:1;
|
||||
int context;
|
||||
int break_opt;
|
||||
|
||||
141
dir.c
141
dir.c
@@ -7,12 +7,17 @@
|
||||
*/
|
||||
#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;
|
||||
@@ -29,8 +34,9 @@ int common_prefix(const char **pathspec)
|
||||
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;
|
||||
|
||||
3
dir.h
3
dir.h
@@ -33,7 +33,8 @@ struct dir_struct {
|
||||
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 */
|
||||
|
||||
51
entry.c
51
entry.c
@@ -62,26 +62,33 @@ static int create_file(const char *path, unsigned int mode)
|
||||
return open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
|
||||
}
|
||||
|
||||
static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned long *size)
|
||||
{
|
||||
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);
|
||||
|
||||
@@ -291,7 +291,7 @@ do
|
||||
<"$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"
|
||||
|
||||
@@ -17,6 +17,7 @@ newbranch=
|
||||
newbranch_log=
|
||||
merge=
|
||||
quiet=
|
||||
v=-v
|
||||
LF='
|
||||
'
|
||||
while [ "$#" != "0" ]; do
|
||||
@@ -47,6 +48,7 @@ while [ "$#" != "0" ]; do
|
||||
;;
|
||||
"-q")
|
||||
quiet=1
|
||||
v=
|
||||
;;
|
||||
--)
|
||||
break
|
||||
@@ -197,7 +199,7 @@ fi
|
||||
|
||||
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) || (
|
||||
@@ -210,7 +212,7 @@ else
|
||||
# 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 &&
|
||||
@@ -221,7 +223,7 @@ else
|
||||
# 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
|
||||
'') ;;
|
||||
*)
|
||||
|
||||
13
git-clean.sh
13
git-clean.sh
@@ -3,9 +3,10 @@
|
||||
# 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
|
||||
@@ -19,6 +20,7 @@ require_work_tree
|
||||
ignored=
|
||||
ignoredonly=
|
||||
cleandir=
|
||||
disabled="`git-config --bool clean.requireForce`"
|
||||
rmf="rm -f --"
|
||||
rmrf="rm -rf --"
|
||||
rm_refuse="echo Not removing"
|
||||
@@ -30,7 +32,11 @@ do
|
||||
-d)
|
||||
cleandir=1
|
||||
;;
|
||||
-f)
|
||||
disabled=
|
||||
;;
|
||||
-n)
|
||||
disabled=
|
||||
rmf="echo Would remove"
|
||||
rmrf="echo Would remove"
|
||||
rm_refuse="echo Would not remove"
|
||||
@@ -58,6 +64,11 @@ do
|
||||
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
|
||||
|
||||
@@ -13,6 +13,14 @@
|
||||
|
||||
#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 */
|
||||
|
||||
43
git-fetch.sh
43
git-fetch.sh
@@ -177,9 +177,34 @@ fetch_all_at_once () {
|
||||
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
|
||||
) |
|
||||
(
|
||||
@@ -239,16 +264,8 @@ fetch_per_ref () {
|
||||
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"
|
||||
|
||||
@@ -71,7 +71,7 @@ then
|
||||
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.
|
||||
|
||||
1
git.c
1
git.c
@@ -238,6 +238,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
|
||||
{ "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 },
|
||||
|
||||
@@ -96,12 +96,14 @@ Perl interface to Git
|
||||
|
||||
%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 {} ';'
|
||||
|
||||
@@ -387,6 +387,10 @@ div.diff.incomplete {
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
div.diff.nodifferences {
|
||||
font-weight: bold;
|
||||
color: #600000;
|
||||
}
|
||||
|
||||
div.index_include {
|
||||
border: solid #d9d8d1;
|
||||
|
||||
@@ -2398,6 +2398,7 @@ sub git_patchset_body {
|
||||
my ($fd, $difftree, $hash, $hash_parent) = @_;
|
||||
|
||||
my $patch_idx = 0;
|
||||
my $patch_number = 0;
|
||||
my $patch_line;
|
||||
my $diffinfo;
|
||||
my (%from, %to);
|
||||
@@ -2419,6 +2420,7 @@ sub git_patchset_body {
|
||||
# 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
|
||||
@@ -2581,6 +2583,7 @@ sub git_patchset_body {
|
||||
} continue {
|
||||
print "</div>\n"; # class="patch"
|
||||
}
|
||||
print "<div class=\"diff nodifferences\">No differences found</div>\n" if (!$patch_number);
|
||||
|
||||
print "</div>\n"; # class="patchset"
|
||||
}
|
||||
|
||||
184
index-pack.c
184
index-pack.c
@@ -6,15 +6,17 @@
|
||||
#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];
|
||||
@@ -22,7 +24,7 @@ struct object_entry
|
||||
|
||||
union delta_base {
|
||||
unsigned char sha1[20];
|
||||
unsigned long offset;
|
||||
off_t offset;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -46,45 +48,14 @@ static int nr_resolved_deltas;
|
||||
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. */
|
||||
@@ -127,8 +98,13 @@ static void use(int bytes)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -216,10 +192,13 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size)
|
||||
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)
|
||||
@@ -415,7 +396,7 @@ static int compare_delta_entry(const void *a, const void *b)
|
||||
/* 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;
|
||||
@@ -427,7 +408,7 @@ static void parse_pack_objects(unsigned char *sha1)
|
||||
* - 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);
|
||||
@@ -440,11 +421,11 @@ static void parse_pack_objects(unsigned char *sha1)
|
||||
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();
|
||||
@@ -476,7 +457,7 @@ static void parse_pack_objects(unsigned char *sha1)
|
||||
* 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;
|
||||
@@ -508,14 +489,11 @@ static void parse_pack_objects(unsigned char *sha1)
|
||||
}
|
||||
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;
|
||||
@@ -536,6 +514,7 @@ static int write_compressed(int fd, void *in, unsigned int size)
|
||||
|
||||
size = stream.total_out;
|
||||
write_or_die(fd, out, size);
|
||||
*obj_crc = crc32(*obj_crc, out, size);
|
||||
free(out);
|
||||
return size;
|
||||
}
|
||||
@@ -556,8 +535,10 @@ static void append_obj_to_pack(const unsigned char *sha1, void *buf,
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -571,7 +552,7 @@ static int delta_pos_compare(const void *_a, const void *_b)
|
||||
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
|
||||
@@ -616,12 +597,9 @@ static void fix_unresolved_deltas(int nr_unresolved)
|
||||
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)
|
||||
@@ -655,6 +633,9 @@ 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);
|
||||
@@ -865,6 +894,15 @@ int main(int argc, char **argv)
|
||||
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;
|
||||
@@ -904,10 +942,13 @@ int main(int argc, char **argv)
|
||||
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;
|
||||
@@ -917,17 +958,16 @@ int main(int argc, char **argv)
|
||||
(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);
|
||||
|
||||
@@ -25,6 +25,37 @@ static void process_blob(struct rev_info *revs,
|
||||
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,
|
||||
@@ -56,6 +87,9 @@ static void process_tree(struct rev_info *revs,
|
||||
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),
|
||||
|
||||
@@ -8,8 +8,11 @@ static const char *alternate_index_output;
|
||||
|
||||
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]) {
|
||||
close(lock_file_list->fd);
|
||||
unlink(lock_file_list->filename);
|
||||
}
|
||||
@@ -29,6 +32,7 @@ static int lock_file(struct lock_file *lk, const char *path)
|
||||
sprintf(lk->filename, "%s.lock", path);
|
||||
lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
|
||||
if (0 <= lk->fd) {
|
||||
lk->owner = getpid();
|
||||
if (!lk->on_list) {
|
||||
lk->next = lock_file_list;
|
||||
lock_file_list = lk;
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include "unpack-trees.h"
|
||||
#include "path-list.h"
|
||||
#include "xdiff-interface.h"
|
||||
#include "interpolate.h"
|
||||
#include "attr.h"
|
||||
|
||||
static int subtree_merge;
|
||||
|
||||
@@ -645,6 +647,384 @@ static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
|
||||
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)
|
||||
@@ -673,30 +1053,11 @@ static struct merge_file_info merge_file(struct diff_filespec *o,
|
||||
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");
|
||||
|
||||
23
object.c
23
object.c
@@ -105,11 +105,13 @@ static void grow_object_hash(void)
|
||||
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);
|
||||
|
||||
@@ -118,25 +120,14 @@ void created_object(const unsigned char *sha1, struct object *obj)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
2
object.h
2
object.h
@@ -46,7 +46,7 @@ extern struct object_refs *lookup_object_refs(struct object *);
|
||||
/** 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);
|
||||
|
||||
@@ -40,7 +40,7 @@ static int verify_packfile(struct packed_git *p,
|
||||
* 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;
|
||||
@@ -79,7 +79,7 @@ static void show_pack_info(struct packed_git *p)
|
||||
{
|
||||
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++) {
|
||||
|
||||
@@ -247,16 +247,19 @@ static struct pack_list * pack_list_difference(const struct pack_list *A,
|
||||
|
||||
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 */
|
||||
@@ -265,14 +268,14 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -352,28 +355,31 @@ static int is_superset(struct pack_list *pl, struct llist *list)
|
||||
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;
|
||||
@@ -535,7 +541,7 @@ static void scan_alt_odb_packs(void)
|
||||
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))
|
||||
@@ -544,11 +550,12 @@ static struct pack_list * add_pack(struct packed_git *p)
|
||||
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);
|
||||
|
||||
106
progress.c
Normal file
106
progress.c
Normal file
@@ -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);
|
||||
}
|
||||
21
progress.h
Normal file
21
progress.h
Normal file
@@ -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
|
||||
35
read-cache.c
35
read-cache.c
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "cache-tree.h"
|
||||
#include "refs.h"
|
||||
|
||||
/* Index extensions.
|
||||
*
|
||||
@@ -91,6 +92,23 @@ static int ce_compare_link(struct cache_entry *ce, size_t expected_size)
|
||||
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) {
|
||||
@@ -102,6 +120,9 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
|
||||
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;
|
||||
}
|
||||
@@ -127,6 +148,12 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
|
||||
(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));
|
||||
}
|
||||
@@ -334,10 +361,14 @@ int add_file_to_cache(const char *path, int verbose)
|
||||
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);
|
||||
|
||||
190
refs.c
190
refs.c
@@ -47,22 +47,7 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
|
||||
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;
|
||||
@@ -71,11 +56,94 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -142,7 +210,7 @@ static void read_packed_refs(FILE *f, struct cached_refs *cached_refs)
|
||||
!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)
|
||||
@@ -201,7 +269,7 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
|
||||
free(ref);
|
||||
closedir(dir);
|
||||
}
|
||||
return list;
|
||||
return sort_ref_list(list);
|
||||
}
|
||||
|
||||
static struct ref_list *get_loose_refs(void)
|
||||
@@ -215,6 +283,86 @@ 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)
|
||||
{
|
||||
|
||||
3
refs.h
3
refs.h
@@ -60,4 +60,7 @@ extern int check_ref_format(const char *target);
|
||||
/** 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 */
|
||||
|
||||
220
sha1_file.c
220
sha1_file.c
@@ -13,6 +13,7 @@
|
||||
#include "commit.h"
|
||||
#include "tag.h"
|
||||
#include "tree.h"
|
||||
#include "refs.h"
|
||||
|
||||
#ifndef O_NOATIME
|
||||
#if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
|
||||
@@ -445,7 +446,7 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
|
||||
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;
|
||||
|
||||
@@ -463,21 +464,23 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
|
||||
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) {
|
||||
@@ -487,21 +490,51 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -615,11 +648,11 @@ static int open_packed_git_1(struct packed_git *p)
|
||||
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))
|
||||
@@ -1138,6 +1171,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,
|
||||
@@ -1159,7 +1229,7 @@ static off_t get_delta_base(struct packed_git *p,
|
||||
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);
|
||||
@@ -1201,40 +1271,8 @@ static int packed_delta_info(struct packed_git *p,
|
||||
* 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;
|
||||
}
|
||||
@@ -1536,37 +1574,60 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
|
||||
return data;
|
||||
}
|
||||
|
||||
uint32_t num_packed_objects(const struct packed_git *p)
|
||||
{
|
||||
/* See check_packed_git_idx() */
|
||||
return (uint32_t)((p->index_size - 20 - 20 - 4*256) / 24);
|
||||
}
|
||||
|
||||
const unsigned char *nth_packed_object_sha1(const struct packed_git *p,
|
||||
uint32_t n)
|
||||
{
|
||||
const unsigned char *index = p->index_data;
|
||||
index += 4 * 256;
|
||||
if (num_packed_objects(p) <= n)
|
||||
if (n >= p->num_objects)
|
||||
return NULL;
|
||||
return index + 24 * n + 4;
|
||||
index += 4 * 256;
|
||||
if (p->index_version == 1) {
|
||||
return index + 24 * n + 4;
|
||||
} else {
|
||||
index += 8;
|
||||
return index + 20 * 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 (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
|
||||
@@ -2287,10 +2348,9 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
|
||||
*/
|
||||
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;
|
||||
@@ -2342,6 +2402,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);
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ static int find_short_packed_object(int len, const unsigned char *match, unsigne
|
||||
|
||||
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;
|
||||
|
||||
68
show-index.c
68
show-index.c
@@ -1,14 +1,26 @@
|
||||
#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]);
|
||||
@@ -16,13 +28,51 @@ int main(int argc, char **argv)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@ test_description='CRLF conversion'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
q_to_nul () {
|
||||
tr Q '\0'
|
||||
}
|
||||
|
||||
append_cr () {
|
||||
sed -e 's/$/Q/' | tr Q '\015'
|
||||
}
|
||||
@@ -20,6 +24,7 @@ test_expect_success setup '
|
||||
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 &&
|
||||
@@ -27,6 +32,7 @@ test_expect_success setup '
|
||||
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 &&
|
||||
@@ -38,7 +44,7 @@ test_expect_success setup '
|
||||
|
||||
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 &&
|
||||
|
||||
@@ -62,7 +68,7 @@ test_expect_success 'update with 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 &&
|
||||
|
||||
@@ -86,7 +92,7 @@ test_expect_success 'update with 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 &&
|
||||
|
||||
@@ -110,7 +116,7 @@ test_expect_success 'checkout with autocrlf=true' '
|
||||
|
||||
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 &&
|
||||
|
||||
@@ -136,7 +142,7 @@ test_expect_success 'checkout with autocrlf=input' '
|
||||
|
||||
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 &&
|
||||
|
||||
@@ -149,7 +155,7 @@ test_expect_success 'apply patch (autocrlf=input)' '
|
||||
|
||||
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 &&
|
||||
|
||||
@@ -162,7 +168,7 @@ test_expect_success 'apply patch --cached (autocrlf=input)' '
|
||||
|
||||
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 &&
|
||||
|
||||
@@ -176,7 +182,7 @@ test_expect_success 'apply patch --index (autocrlf=input)' '
|
||||
|
||||
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 &&
|
||||
|
||||
@@ -189,7 +195,7 @@ test_expect_success 'apply patch (autocrlf=true)' '
|
||||
|
||||
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 &&
|
||||
|
||||
@@ -202,7 +208,7 @@ test_expect_success 'apply patch --cached (autocrlf=true)' '
|
||||
|
||||
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 &&
|
||||
|
||||
@@ -214,4 +220,74 @@ test_expect_success 'apply patch --index (autocrlf=true)' '
|
||||
}
|
||||
'
|
||||
|
||||
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
|
||||
|
||||
85
t/t3040-subprojects-basic.sh
Executable file
85
t/t3040-subprojects-basic.sh
Executable file
@@ -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
|
||||
@@ -104,4 +104,10 @@ test_expect_success 'add ignored ones with -f' '
|
||||
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
|
||||
|
||||
97
t/t4020-diff-external.sh
Executable file
97
t/t4020-diff-external.sh
Executable file
@@ -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
|
||||
@@ -15,19 +15,19 @@ commit=$( (echo "Test"; echo) | git commit-tree $tree )
|
||||
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
|
||||
|
||||
|
||||
14
t/t5300-pack-object.sh
Executable file → Normal file
14
t/t5300-pack-object.sh
Executable file → Normal file
@@ -174,7 +174,7 @@ test_expect_success \
|
||||
'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
|
||||
@@ -189,7 +189,7 @@ test_expect_success \
|
||||
'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
|
||||
@@ -210,8 +210,8 @@ test_expect_success \
|
||||
|
||||
test_expect_success \
|
||||
'verify-pack catches mismatched .idx and .pack files' \
|
||||
'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 :;
|
||||
@@ -253,21 +253,21 @@ test_expect_success \
|
||||
|
||||
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 &&
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ test_expect_success \
|
||||
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 &&
|
||||
|
||||
146
t/t5302-pack-index.sh
Executable file
146
t/t5302-pack-index.sh
Executable file
@@ -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
|
||||
89
t/t5502-quickfetch.sh
Executable file
89
t/t5502-quickfetch.sh
Executable file
@@ -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
|
||||
145
t/t6026-merge-attr.sh
Executable file
145
t/t6026-merge-attr.sh
Executable file
@@ -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
|
||||
@@ -22,22 +22,25 @@ add_line_into_file()
|
||||
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 &&
|
||||
|
||||
10
tag.c
10
tag.c
@@ -20,13 +20,9 @@ struct object *deref_tag(struct object *o, const char *warn, int warnlen)
|
||||
|
||||
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) {
|
||||
|
||||
34
test-genrandom.c
Normal file
34
test-genrandom.c
Normal file
@@ -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;
|
||||
}
|
||||
23
tree.c
23
tree.c
@@ -127,12 +127,8 @@ int read_tree(struct tree *tree, int stage, const char **match)
|
||||
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) {
|
||||
@@ -143,6 +139,14 @@ struct tree *lookup_tree(const unsigned char *sha1)
|
||||
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;
|
||||
@@ -152,8 +156,11 @@ static void track_tree_refs(struct tree *item)
|
||||
|
||||
/* 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;
|
||||
@@ -162,6 +169,8 @@ static void track_tree_refs(struct tree *item)
|
||||
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
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "tree-walk.h"
|
||||
#include "cache-tree.h"
|
||||
#include "unpack-trees.h"
|
||||
#include "progress.h"
|
||||
|
||||
#define DBRT_DEBUG 1
|
||||
|
||||
@@ -288,36 +289,13 @@ static void unlink_entry(char *name)
|
||||
}
|
||||
}
|
||||
|
||||
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++) {
|
||||
@@ -326,35 +304,17 @@ static void check_updates(struct cache_entry **src, int nr,
|
||||
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);
|
||||
@@ -366,10 +326,8 @@ static void check_updates(struct cache_entry **src, int nr,
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user