Merge commit '520d7e278cfd25057e883575060b7378dfab61dc'

This commit is contained in:
Johannes Sixt
2007-05-19 23:04:17 +02:00
89 changed files with 4421 additions and 1105 deletions

2
.gitignore vendored
View File

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

View File

@@ -1,7 +1,6 @@
*.xml
*.html
*.1
*.7
*.[1-8]
*.made
howto-index.txt
doc.dep

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

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

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

View File

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

View 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;
}

View File

@@ -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
View 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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

@@ -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);
}

View File

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

View File

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

View File

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

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

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

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

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

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

View File

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

View File

@@ -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
'') ;;
*)

View File

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

View File

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

View File

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

View File

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

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

View File

@@ -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 {} ';'

View File

@@ -387,6 +387,10 @@ div.diff.incomplete {
color: #cccccc;
}
div.diff.nodifferences {
font-weight: bold;
color: #600000;
}
div.index_include {
border: solid #d9d8d1;

View File

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

View File

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

View File

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

View File

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

View File

@@ -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");

View File

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

View File

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

View File

@@ -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++) {

View File

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

View File

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

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

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

View File

@@ -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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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