Merge branch 'lt/tree' into jc/lt-tree-n-cache-tree
* lt/tree: (98 commits) Remove "tree->entries" tree-entry list from tree parser Switch "read_tree_recursive()" over to tree-walk functionality Make "tree_entry" have a SHA1 instead of a union of object pointers Add raw tree buffer info to "struct tree" Don't use "sscanf()" for tree mode scanning git-fetch: avoid using "case ... in (arm)" mailinfo: skip bogus UNIX From line inside body mailinfo: More carefully parse header lines in read_one_header_line() Allow in body headers beyond the in body header prefix. More accurately detect header lines in read_one_header_line In handle_body only read a line if we don't already have one. Refactor commit messge handling. Move B and Q decoding into check header. Make read_one_header_line return a flag not a length. Fix memory leak in "git rev-list --objects" gitview: Move the console error messages to message dialog gitview: Add key binding for F5. Let git-clone to pass --template=dir option to git-init-db. Make cvsexportcommit create parent directories as needed. Document current cvsexportcommit limitations. ... Conflicts: Makefile, builtin.h, git.c are trivially resolved. builtin-read-tree.c needed adjustment for the tree parser change.
This commit is contained in:
commit
0a2586c807
2
.gitignore
vendored
2
.gitignore
vendored
@ -77,6 +77,7 @@ git-prune
|
|||||||
git-prune-packed
|
git-prune-packed
|
||||||
git-pull
|
git-pull
|
||||||
git-push
|
git-push
|
||||||
|
git-quiltimport
|
||||||
git-read-tree
|
git-read-tree
|
||||||
git-rebase
|
git-rebase
|
||||||
git-receive-pack
|
git-receive-pack
|
||||||
@ -115,6 +116,7 @@ git-update-index
|
|||||||
git-update-ref
|
git-update-ref
|
||||||
git-update-server-info
|
git-update-server-info
|
||||||
git-upload-pack
|
git-upload-pack
|
||||||
|
git-upload-tar
|
||||||
git-var
|
git-var
|
||||||
git-verify-pack
|
git-verify-pack
|
||||||
git-verify-tag
|
git-verify-tag
|
||||||
|
@ -7,6 +7,7 @@ MAN7_TXT=git.txt
|
|||||||
DOC_HTML=$(patsubst %.txt,%.html,$(MAN1_TXT) $(MAN7_TXT))
|
DOC_HTML=$(patsubst %.txt,%.html,$(MAN1_TXT) $(MAN7_TXT))
|
||||||
|
|
||||||
ARTICLES = tutorial
|
ARTICLES = tutorial
|
||||||
|
ARTICLES += tutorial-2
|
||||||
ARTICLES += core-tutorial
|
ARTICLES += core-tutorial
|
||||||
ARTICLES += cvs-migration
|
ARTICLES += cvs-migration
|
||||||
ARTICLES += diffcore
|
ARTICLES += diffcore
|
||||||
@ -51,9 +52,9 @@ man1: $(DOC_MAN1)
|
|||||||
man7: $(DOC_MAN7)
|
man7: $(DOC_MAN7)
|
||||||
|
|
||||||
install: man
|
install: man
|
||||||
$(INSTALL) -d -m755 $(DESTDIR)/$(man1) $(DESTDIR)/$(man7)
|
$(INSTALL) -d -m755 $(DESTDIR)$(man1) $(DESTDIR)$(man7)
|
||||||
$(INSTALL) $(DOC_MAN1) $(DESTDIR)/$(man1)
|
$(INSTALL) $(DOC_MAN1) $(DESTDIR)$(man1)
|
||||||
$(INSTALL) $(DOC_MAN7) $(DESTDIR)/$(man7)
|
$(INSTALL) $(DOC_MAN7) $(DESTDIR)$(man7)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -14,11 +14,13 @@ DESCRIPTION
|
|||||||
A simple wrapper for git-update-index to add files to the index,
|
A simple wrapper for git-update-index to add files to the index,
|
||||||
for people used to do "cvs add".
|
for people used to do "cvs add".
|
||||||
|
|
||||||
|
It only adds non-ignored files, to add ignored files use
|
||||||
|
"git update-index --add".
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
-------
|
-------
|
||||||
<file>...::
|
<file>...::
|
||||||
Files to add to the index.
|
Files to add to the index (see gitlink:git-ls-files[1]).
|
||||||
|
|
||||||
-n::
|
-n::
|
||||||
Don't actually add the file(s), just show if they exist.
|
Don't actually add the file(s), just show if they exist.
|
||||||
@ -68,6 +70,7 @@ git-add git-*.sh::
|
|||||||
See Also
|
See Also
|
||||||
--------
|
--------
|
||||||
gitlink:git-rm[1]
|
gitlink:git-rm[1]
|
||||||
|
gitlink:git-ls-files[1]
|
||||||
|
|
||||||
Author
|
Author
|
||||||
------
|
------
|
||||||
|
@ -43,6 +43,9 @@ OPTIONS
|
|||||||
|
|
||||||
<branchname>::
|
<branchname>::
|
||||||
The name of the branch to create or delete.
|
The name of the branch to create or delete.
|
||||||
|
The new branch name must pass all checks defined by
|
||||||
|
gitlink:git-check-ref-format[1]. Some of these checks
|
||||||
|
may restrict the characters allowed in a branch name.
|
||||||
|
|
||||||
<start-point>::
|
<start-point>::
|
||||||
The new branch will be created with a HEAD equal to this. It may
|
The new branch will be created with a HEAD equal to this. It may
|
||||||
|
@ -8,12 +8,12 @@ git-cat-file - Provide content or type information for repository objects
|
|||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
'git-cat-file' [-t | -s | -e | <type>] <object>
|
'git-cat-file' [-t | -s | -e | -p | <type>] <object>
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
Provides content or type of objects in the repository. The type
|
Provides content or type of objects in the repository. The type
|
||||||
is required unless '-t' is used to find the object type,
|
is required unless '-t' or '-p' is used to find the object type,
|
||||||
or '-s' is used to find the object size.
|
or '-s' is used to find the object size.
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
@ -33,6 +33,9 @@ OPTIONS
|
|||||||
Suppress all output; instead exit with zero status if <object>
|
Suppress all output; instead exit with zero status if <object>
|
||||||
exists and is a valid object.
|
exists and is a valid object.
|
||||||
|
|
||||||
|
-p::
|
||||||
|
Pretty-print the contents of <object> based on its type.
|
||||||
|
|
||||||
<type>::
|
<type>::
|
||||||
Typically this matches the real type of <object> but asking
|
Typically this matches the real type of <object> but asking
|
||||||
for a type that can trivially be dereferenced from the given
|
for a type that can trivially be dereferenced from the given
|
||||||
@ -49,6 +52,8 @@ If '-s' is specified, the size of the <object> in bytes.
|
|||||||
|
|
||||||
If '-e' is specified, no output.
|
If '-e' is specified, no output.
|
||||||
|
|
||||||
|
If '-p' is specified, the contents of <object> are pretty-printed.
|
||||||
|
|
||||||
Otherwise the raw (though uncompressed) contents of the <object> will
|
Otherwise the raw (though uncompressed) contents of the <object> will
|
||||||
be returned.
|
be returned.
|
||||||
|
|
||||||
|
@ -45,6 +45,8 @@ refname expressions (see gitlink:git-rev-parse[1]). Namely:
|
|||||||
|
|
||||||
. colon `:` is used as in `srcref:dstref` to mean "use srcref\'s
|
. colon `:` is used as in `srcref:dstref` to mean "use srcref\'s
|
||||||
value and store it in dstref" in fetch and push operations.
|
value and store it in dstref" in fetch and push operations.
|
||||||
|
It may also be used to select a specific object such as with
|
||||||
|
gitlink:git-cat-file[1] "git-cat-file blob v1.3.3:refs.c".
|
||||||
|
|
||||||
|
|
||||||
GIT
|
GIT
|
||||||
|
@ -35,7 +35,10 @@ OPTIONS
|
|||||||
Force a re-read of everything.
|
Force a re-read of everything.
|
||||||
|
|
||||||
-b::
|
-b::
|
||||||
Create a new branch and start it at <branch>.
|
Create a new branch named <new_branch> and start it at
|
||||||
|
<branch>. The new branch name must pass all checks defined
|
||||||
|
by gitlink:git-check-ref-format[1]. Some of these checks
|
||||||
|
may restrict the characters allowed in a branch name.
|
||||||
|
|
||||||
-m::
|
-m::
|
||||||
If you have local modifications to one or more files that
|
If you have local modifications to one or more files that
|
||||||
|
@ -9,8 +9,8 @@ git-clone - Clones a repository
|
|||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
[verse]
|
[verse]
|
||||||
'git-clone' [-l [-s]] [-q] [-n] [--bare] [-o <name>] [-u <upload-pack>]
|
'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
|
||||||
[--reference <repository>]
|
[-o <name>] [-u <upload-pack>] [--reference <repository>]
|
||||||
<repository> [<directory>]
|
<repository> [<directory>]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
@ -89,6 +89,11 @@ OPTIONS
|
|||||||
the command to specify non-default path for the command
|
the command to specify non-default path for the command
|
||||||
run on the other end.
|
run on the other end.
|
||||||
|
|
||||||
|
--template=<template_directory>::
|
||||||
|
Specify the directory from which templates will be used;
|
||||||
|
if unset the templates are taken from the installation
|
||||||
|
defined default, typically `/usr/share/git-core/templates`.
|
||||||
|
|
||||||
<repository>::
|
<repository>::
|
||||||
The (possibly remote) repository to clone from. It can
|
The (possibly remote) repository to clone from. It can
|
||||||
be any URL git-fetch supports.
|
be any URL git-fetch supports.
|
||||||
|
391
Documentation/tutorial-2.txt
Normal file
391
Documentation/tutorial-2.txt
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
A tutorial introduction to git: part two
|
||||||
|
========================================
|
||||||
|
|
||||||
|
You should work through link:tutorial.html[A tutorial introduction to
|
||||||
|
git] before reading this tutorial.
|
||||||
|
|
||||||
|
The goal of this tutorial is to introduce two fundamental pieces of
|
||||||
|
git's architecture--the object database and the index file--and to
|
||||||
|
provide the reader with everything necessary to understand the rest
|
||||||
|
of the git documentation.
|
||||||
|
|
||||||
|
The git object database
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Let's start a new project and create a small amount of history:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ mkdir test-project
|
||||||
|
$ cd test-project
|
||||||
|
$ git init-db
|
||||||
|
defaulting to local storage area
|
||||||
|
$ echo 'hello world' > file.txt
|
||||||
|
$ git add .
|
||||||
|
$ git commit -a -m "initial commit"
|
||||||
|
Committing initial tree 92b8b694ffb1675e5975148e1121810081dbdffe
|
||||||
|
$ echo 'hello world!' >file.txt
|
||||||
|
$ git commit -a -m "add emphasis"
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
What are the 40 digits of hex that git responded to the first commit
|
||||||
|
with?
|
||||||
|
|
||||||
|
We saw in part one of the tutorial that commits have names like this.
|
||||||
|
It turns out that every object in the git history is stored under
|
||||||
|
such a 40-digit hex name. That name is the SHA1 hash of the object's
|
||||||
|
contents; among other things, this ensures that git will never store
|
||||||
|
the same data twice (since identical data is given an identical SHA1
|
||||||
|
name), and that the contents of a git object will never change (since
|
||||||
|
that would change the object's name as well).
|
||||||
|
|
||||||
|
We can ask git about this particular object with the cat-file
|
||||||
|
command--just cut-and-paste from the reply to the initial commit, to
|
||||||
|
save yourself typing all 40 hex digits:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ git cat-file -t 92b8b694ffb1675e5975148e1121810081dbdffe
|
||||||
|
tree
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
A tree can refer to one or more "blob" objects, each corresponding to
|
||||||
|
a file. In addition, a tree can also refer to other tree objects,
|
||||||
|
thus creating a directory heirarchy. You can examine the contents of
|
||||||
|
any tree using ls-tree (remember that a long enough initial portion
|
||||||
|
of the SHA1 will also work):
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ git ls-tree 92b8b694
|
||||||
|
100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad file.txt
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
Thus we see that this tree has one file in it. The SHA1 hash is a
|
||||||
|
reference to that file's data:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ git cat-file -t 3b18e512
|
||||||
|
blob
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
A "blob" is just file data, which we can also examine with cat-file:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ git cat-file blob 3b18e512
|
||||||
|
hello world
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
Note that this is the old file data; so the object that git named in
|
||||||
|
its response to the initial tree was a tree with a snapshot of the
|
||||||
|
directory state that was recorded by the first commit.
|
||||||
|
|
||||||
|
All of these objects are stored under their SHA1 names inside the git
|
||||||
|
directory:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ find .git/objects/
|
||||||
|
.git/objects/
|
||||||
|
.git/objects/pack
|
||||||
|
.git/objects/info
|
||||||
|
.git/objects/3b
|
||||||
|
.git/objects/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad
|
||||||
|
.git/objects/92
|
||||||
|
.git/objects/92/b8b694ffb1675e5975148e1121810081dbdffe
|
||||||
|
.git/objects/54
|
||||||
|
.git/objects/54/196cc2703dc165cbd373a65a4dcf22d50ae7f7
|
||||||
|
.git/objects/a0
|
||||||
|
.git/objects/a0/423896973644771497bdc03eb99d5281615b51
|
||||||
|
.git/objects/d0
|
||||||
|
.git/objects/d0/492b368b66bdabf2ac1fd8c92b39d3db916e59
|
||||||
|
.git/objects/c4
|
||||||
|
.git/objects/c4/d59f390b9cfd4318117afde11d601c1085f241
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
and the contents of these files is just the compressed data plus a
|
||||||
|
header identifying their length and their type. The type is either a
|
||||||
|
blob, a tree, a commit, or a tag. We've seen a blob and a tree now,
|
||||||
|
so next we should look at a commit.
|
||||||
|
|
||||||
|
The simplest commit to find is the HEAD commit, which we can find
|
||||||
|
from .git/HEAD:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ cat .git/HEAD
|
||||||
|
ref: refs/heads/master
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
As you can see, this tells us which branch we're currently on, and it
|
||||||
|
tells us this by naming a file under the .git directory, which itself
|
||||||
|
contains a SHA1 name referring to a commit object, which we can
|
||||||
|
examine with cat-file:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ cat .git/refs/heads/master
|
||||||
|
c4d59f390b9cfd4318117afde11d601c1085f241
|
||||||
|
$ git cat-file -t c4d59f39
|
||||||
|
commit
|
||||||
|
$ git cat-file commit c4d59f39
|
||||||
|
tree d0492b368b66bdabf2ac1fd8c92b39d3db916e59
|
||||||
|
parent 54196cc2703dc165cbd373a65a4dcf22d50ae7f7
|
||||||
|
author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143418702 -0500
|
||||||
|
committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143418702 -0500
|
||||||
|
|
||||||
|
add emphasis
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
The "tree" object here refers to the new state of the tree:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ git ls-tree d0492b36
|
||||||
|
100644 blob a0423896973644771497bdc03eb99d5281615b51 file.txt
|
||||||
|
$ git cat-file commit a0423896
|
||||||
|
hello world!
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
and the "parent" object refers to the previous commit:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ git-cat-file commit 54196cc2
|
||||||
|
tree 92b8b694ffb1675e5975148e1121810081dbdffe
|
||||||
|
author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500
|
||||||
|
committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500
|
||||||
|
|
||||||
|
initial commit
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
The tree object is the tree we examined first, and this commit is
|
||||||
|
unusual in that it lacks any parent.
|
||||||
|
|
||||||
|
Most commits have only one parent, but it is also common for a commit
|
||||||
|
to have multiple parents. In that case the commit represents a
|
||||||
|
merge, with the parent references pointing to the heads of the merged
|
||||||
|
branches.
|
||||||
|
|
||||||
|
Besides blobs, trees, and commits, the only remaining type of object
|
||||||
|
is a "tag", which we won't discuss here; refer to gitlink:git-tag[1]
|
||||||
|
for details.
|
||||||
|
|
||||||
|
So now we know how git uses the object database to represent a
|
||||||
|
project's history:
|
||||||
|
|
||||||
|
* "commit" objects refer to "tree" objects representing the
|
||||||
|
snapshot of a directory tree at a particular point in the
|
||||||
|
history, and refer to "parent" commits to show how they're
|
||||||
|
connected into the project history.
|
||||||
|
* "tree" objects represent the state of a single directory,
|
||||||
|
associating directory names to "blob" objects containing file
|
||||||
|
data and "tree" objects containing subdirectory information.
|
||||||
|
* "blob" objects contain file data without any other structure.
|
||||||
|
* References to commit objects at the head of each branch are
|
||||||
|
stored in files under .git/refs/heads/.
|
||||||
|
* The name of the current branch is stored in .git/HEAD.
|
||||||
|
|
||||||
|
Note, by the way, that lots of commands take a tree as an argument.
|
||||||
|
But as we can see above, a tree can be referred to in many different
|
||||||
|
ways--by the SHA1 name for that tree, by the name of a commit that
|
||||||
|
refers to the tree, by the name of a branch whose head refers to that
|
||||||
|
tree, etc.--and most such commands can accept any of these names.
|
||||||
|
|
||||||
|
In command synopses, the word "tree-ish" is sometimes used to
|
||||||
|
designate such an argument.
|
||||||
|
|
||||||
|
The index file
|
||||||
|
--------------
|
||||||
|
|
||||||
|
The primary tool we've been using to create commits is "git commit
|
||||||
|
-a", which creates a commit including every change you've made to
|
||||||
|
your working tree. But what if you want to commit changes only to
|
||||||
|
certain files? Or only certain changes to certain files?
|
||||||
|
|
||||||
|
If we look at the way commits are created under the cover, we'll see
|
||||||
|
that there are more flexible ways creating commits.
|
||||||
|
|
||||||
|
Continuing with our test-project, let's modify file.txt again:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ echo "hello world, again" >>file.txt
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
but this time instead of immediately making the commit, let's take an
|
||||||
|
intermediate step, and ask for diffs along the way to keep track of
|
||||||
|
what's happening:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ git diff
|
||||||
|
--- a/file.txt
|
||||||
|
+++ b/file.txt
|
||||||
|
@@ -1 +1,2 @@
|
||||||
|
hello world!
|
||||||
|
+hello world, again
|
||||||
|
$ git update-index file.txt
|
||||||
|
$ git diff
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
The last diff is empty, but no new commits have been made, and the
|
||||||
|
head still doesn't contain the new line:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ git-diff HEAD
|
||||||
|
diff --git a/file.txt b/file.txt
|
||||||
|
index a042389..513feba 100644
|
||||||
|
--- a/file.txt
|
||||||
|
+++ b/file.txt
|
||||||
|
@@ -1 +1,2 @@
|
||||||
|
hello world!
|
||||||
|
+hello world, again
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
So "git diff" is comparing against something other than the head.
|
||||||
|
The thing that it's comparing against is actually the index file,
|
||||||
|
which is stored in .git/index in a binary format, but whose contents
|
||||||
|
we can examine with ls-files:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ git ls-files --stage
|
||||||
|
100644 513feba2e53ebbd2532419ded848ba19de88ba00 0 file.txt
|
||||||
|
$ git cat-file -t 513feba2
|
||||||
|
blob
|
||||||
|
$ git cat-file blob 513feba2
|
||||||
|
hello world, again
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
So what our "git update-index" did was store a new blob and then put
|
||||||
|
a reference to it in the index file. If we modify the file again,
|
||||||
|
we'll see that the new modifications are reflected in the "git-diff"
|
||||||
|
output:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ echo 'again?' >>file.txt
|
||||||
|
$ git diff
|
||||||
|
index 513feba..ba3da7b 100644
|
||||||
|
--- a/file.txt
|
||||||
|
+++ b/file.txt
|
||||||
|
@@ -1,2 +1,3 @@
|
||||||
|
hello world!
|
||||||
|
hello world, again
|
||||||
|
+again?
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
With the right arguments, git diff can also show us the difference
|
||||||
|
between the working directory and the last commit, or between the
|
||||||
|
index and the last commit:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ git diff HEAD
|
||||||
|
diff --git a/file.txt b/file.txt
|
||||||
|
index a042389..ba3da7b 100644
|
||||||
|
--- a/file.txt
|
||||||
|
+++ b/file.txt
|
||||||
|
@@ -1 +1,3 @@
|
||||||
|
hello world!
|
||||||
|
+hello world, again
|
||||||
|
+again?
|
||||||
|
$ git diff --cached
|
||||||
|
diff --git a/file.txt b/file.txt
|
||||||
|
index a042389..513feba 100644
|
||||||
|
--- a/file.txt
|
||||||
|
+++ b/file.txt
|
||||||
|
@@ -1 +1,2 @@
|
||||||
|
hello world!
|
||||||
|
+hello world, again
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
At any time, we can create a new commit using "git commit" (without
|
||||||
|
the -a option), and verify that the state committed only includes the
|
||||||
|
changes stored in the index file, not the additional change that is
|
||||||
|
still only in our working tree:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ git commit -m "repeat"
|
||||||
|
$ git diff HEAD
|
||||||
|
diff --git a/file.txt b/file.txt
|
||||||
|
index 513feba..ba3da7b 100644
|
||||||
|
--- a/file.txt
|
||||||
|
+++ b/file.txt
|
||||||
|
@@ -1,2 +1,3 @@
|
||||||
|
hello world!
|
||||||
|
hello world, again
|
||||||
|
+again?
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
So by default "git commit" uses the index to create the commit, not
|
||||||
|
the working tree; the -a option to commit tells it to first update
|
||||||
|
the index with all changes in the working tree.
|
||||||
|
|
||||||
|
Finally, it's worth looking at the effect of "git add" on the index
|
||||||
|
file:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ echo "goodbye, world" >closing.txt
|
||||||
|
$ git add closing.txt
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
The effect of the "git add" was to add one entry to the index file:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ git ls-files --stage
|
||||||
|
100644 8b9743b20d4b15be3955fc8d5cd2b09cd2336138 0 closing.txt
|
||||||
|
100644 513feba2e53ebbd2532419ded848ba19de88ba00 0 file.txt
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
And, as you can see with cat-file, this new entry refers to the
|
||||||
|
current contents of the file:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ git cat-file blob a6b11f7a
|
||||||
|
goodbye, word
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
The "status" command is a useful way to get a quick summary of the
|
||||||
|
situation:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ git status
|
||||||
|
#
|
||||||
|
# Updated but not checked in:
|
||||||
|
# (will commit)
|
||||||
|
#
|
||||||
|
# new file: closing.txt
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Changed but not updated:
|
||||||
|
# (use git-update-index to mark for commit)
|
||||||
|
#
|
||||||
|
# modified: file.txt
|
||||||
|
#
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
Since the current state of closing.txt is cached in the index file,
|
||||||
|
it is listed as "updated but not checked in". Since file.txt has
|
||||||
|
changes in the working directory that aren't reflected in the index,
|
||||||
|
it is marked "changed but not updated". At this point, running "git
|
||||||
|
commit" would create a commit that added closing.txt (with its new
|
||||||
|
contents), but that didn't modify file.txt.
|
||||||
|
|
||||||
|
Also, note that a bare "git diff" shows the changes to file.txt, but
|
||||||
|
not the addition of closing.txt, because the version of closing.txt
|
||||||
|
in the index file is identical to the one in the working directory.
|
||||||
|
|
||||||
|
In addition to being the staging area for new commits, the index file
|
||||||
|
is also populated from the object database when checking out a
|
||||||
|
branch, and is used to hold the trees involved in a merge operation.
|
||||||
|
See the link:core-tutorial.txt[core tutorial] and the relevant man
|
||||||
|
pages for details.
|
||||||
|
|
||||||
|
What next?
|
||||||
|
----------
|
||||||
|
|
||||||
|
At this point you should know everything necessary to read the man
|
||||||
|
pages for any of the git commands; one good place to start would be
|
||||||
|
with the commands mentioned in link:everyday.html[Everyday git]. You
|
||||||
|
should be able to find any unknown jargon in the
|
||||||
|
link:glossary.html[Glosssay].
|
||||||
|
|
||||||
|
The link:cvs-migration.html[CVS migration] document explains how to
|
||||||
|
import a CVS repository into git, and shows how to use git in a
|
||||||
|
CVS-like way.
|
||||||
|
|
||||||
|
For some interesting examples of git use, see the
|
||||||
|
link:howto-index.html[howtos].
|
||||||
|
|
||||||
|
For git developers, the link:core-tutorial.html[Core tutorial] goes
|
||||||
|
into detail on the lower-level git mechanisms involved in, for
|
||||||
|
example, creating a new commit.
|
@ -80,13 +80,13 @@ file; just remove it, then commit.
|
|||||||
At any point you can view the history of your changes using
|
At any point you can view the history of your changes using
|
||||||
|
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
$ git whatchanged
|
$ git log
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
|
|
||||||
If you also want to see complete diffs at each step, use
|
If you also want to see complete diffs at each step, use
|
||||||
|
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
$ git whatchanged -p
|
$ git log -p
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
|
|
||||||
Managing branches
|
Managing branches
|
||||||
@ -216,7 +216,7 @@ This actually pulls changes from the branch in Bob's repository named
|
|||||||
"master". Alice could request a different branch by adding the name
|
"master". Alice could request a different branch by adding the name
|
||||||
of the branch to the end of the git pull command line.
|
of the branch to the end of the git pull command line.
|
||||||
|
|
||||||
This merges Bob's changes into her repository; "git whatchanged" will
|
This merges Bob's changes into her repository; "git log" will
|
||||||
now show the new commits. If Alice has made her own changes in the
|
now show the new commits. If Alice has made her own changes in the
|
||||||
meantime, then Bob's changes will be merged in, and she will need to
|
meantime, then Bob's changes will be merged in, and she will need to
|
||||||
manually fix any conflicts.
|
manually fix any conflicts.
|
||||||
@ -234,7 +234,7 @@ named bob-incoming. (Unlike git pull, git fetch just fetches a copy
|
|||||||
of Bob's line of development without doing any merging). Then
|
of Bob's line of development without doing any merging). Then
|
||||||
|
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
$ git whatchanged -p master..bob-incoming
|
$ git log -p master..bob-incoming
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
shows a list of all the changes that Bob made since he branched from
|
shows a list of all the changes that Bob made since he branched from
|
||||||
@ -288,102 +288,179 @@ Git can also be used in a CVS-like mode, with a central repository
|
|||||||
that various users push changes to; see gitlink:git-push[1] and
|
that various users push changes to; see gitlink:git-push[1] and
|
||||||
link:cvs-migration.html[git for CVS users].
|
link:cvs-migration.html[git for CVS users].
|
||||||
|
|
||||||
Keeping track of history
|
Exploring history
|
||||||
------------------------
|
-----------------
|
||||||
|
|
||||||
Git history is represented as a series of interrelated commits. The
|
Git history is represented as a series of interrelated commits. We
|
||||||
most recent commit in the currently checked-out branch can always be
|
have already seen that the git log command can list those commits.
|
||||||
referred to as HEAD, and the "parent" of any commit can always be
|
Note that first line of each git log entry also gives a name for the
|
||||||
referred to by appending a caret, "^", to the end of the name of the
|
commit:
|
||||||
commit. So, for example,
|
|
||||||
|
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
git diff HEAD^ HEAD
|
$ git log
|
||||||
|
commit c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
|
||||||
|
Author: Junio C Hamano <junkio@cox.net>
|
||||||
|
Date: Tue May 16 17:18:22 2006 -0700
|
||||||
|
|
||||||
|
merge-base: Clarify the comments on post processing.
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
shows the difference between the most-recently checked-in state of
|
We can give this name to git show to see the details about this
|
||||||
the tree and the previous state, and
|
commit.
|
||||||
|
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
git diff HEAD^^ HEAD^
|
$ git show c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
shows the difference between that previous state and the state two
|
But there other ways to refer to commits. You can use any initial
|
||||||
commits ago. Also, HEAD~5 can be used as a shorthand for HEAD{caret}{caret}{caret}{caret}{caret},
|
part of the name that is long enough to uniquely identify the commit:
|
||||||
and more generally HEAD~n can refer to the nth previous commit.
|
|
||||||
Commits representing merges have more than one parent, and you can
|
|
||||||
specify which parent to follow in that case; see
|
|
||||||
gitlink:git-rev-parse[1].
|
|
||||||
|
|
||||||
The name of a branch can also be used to refer to the most recent
|
|
||||||
commit on that branch; so you can also say things like
|
|
||||||
|
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
git diff HEAD experimental
|
$ git show c82a22c39c # the first few characters of the name are
|
||||||
|
# usually enough
|
||||||
|
$ git show HEAD # the tip of the current branch
|
||||||
|
$ git show experimental # the tip of the "experimental" branch
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
to see the difference between the most-recently committed tree in
|
Every commit has at least one "parent" commit, which points to the
|
||||||
the current branch and the most-recently committed tree in the
|
previous state of the project:
|
||||||
experimental branch.
|
|
||||||
|
|
||||||
But you may find it more useful to see the list of commits made in
|
|
||||||
the experimental branch but not in the current branch, and
|
|
||||||
|
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
git whatchanged HEAD..experimental
|
$ git show HEAD^ # to see the parent of HEAD
|
||||||
|
$ git show HEAD^^ # to see the grandparent of HEAD
|
||||||
|
$ git show HEAD~4 # to see the great-great grandparent of HEAD
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
will do that, just as
|
Note that merge commits may have more than one parent:
|
||||||
|
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
git whatchanged experimental..HEAD
|
$ git show HEAD^1 # show the first parent of HEAD (same as HEAD^)
|
||||||
|
$ git show HEAD^2 # show the second parent of HEAD
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
will show the list of commits made on the HEAD but not included in
|
You can also give commits names of your own; after running
|
||||||
experimental.
|
|
||||||
|
|
||||||
You can also give commits convenient names of your own: after running
|
|
||||||
|
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
$ git-tag v2.5 HEAD^^
|
$ git-tag v2.5 1b2e1d63ff
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
you can refer to HEAD^^ by the name "v2.5". If you intend to share
|
you can refer to 1b2e1d63ff by the name "v2.5". If you intend to
|
||||||
this name with other people (for example, to identify a release
|
share this name with other people (for example, to identify a release
|
||||||
version), you should create a "tag" object, and perhaps sign it; see
|
version), you should create a "tag" object, and perhaps sign it; see
|
||||||
gitlink:git-tag[1] for details.
|
gitlink:git-tag[1] for details.
|
||||||
|
|
||||||
You can revisit the old state of a tree, and make further
|
Any git command that needs to know a commit can take any of these
|
||||||
modifications if you wish, using git branch: the command
|
names. For example:
|
||||||
|
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
$ git branch stable-release v2.5
|
$ git diff v2.5 HEAD # compare the current HEAD to v2.5
|
||||||
|
$ git branch stable v2.5 # start a new branch named "stable" based
|
||||||
|
# at v2.5
|
||||||
|
$ git reset --hard HEAD^ # reset your current branch and working
|
||||||
|
# directory its state at HEAD^
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
will create a new branch named "stable-release" starting from the
|
Be careful with that last command: in addition to losing any changes
|
||||||
commit which you tagged with the name v2.5.
|
in the working directory, it will also remove all later commits from
|
||||||
|
this branch. If this branch is the only branch containing those
|
||||||
You can reset the state of any branch to an earlier commit at any
|
commits, they will be lost. (Also, don't use "git reset" on a
|
||||||
time with
|
|
||||||
|
|
||||||
-------------------------------------
|
|
||||||
$ git reset --hard v2.5
|
|
||||||
-------------------------------------
|
|
||||||
|
|
||||||
This will remove all later commits from this branch and reset the
|
|
||||||
working tree to the state it had when the given commit was made. If
|
|
||||||
this branch is the only branch containing the later commits, those
|
|
||||||
later changes will be lost. Don't use "git reset" on a
|
|
||||||
publicly-visible branch that other developers pull from, as git will
|
publicly-visible branch that other developers pull from, as git will
|
||||||
be confused by history that disappears in this way.
|
be confused by history that disappears in this way.)
|
||||||
|
|
||||||
|
The git grep command can search for strings in any version of your
|
||||||
|
project, so
|
||||||
|
|
||||||
|
-------------------------------------
|
||||||
|
$ git grep "hello" v2.5
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
searches for all occurences of "hello" in v2.5.
|
||||||
|
|
||||||
|
If you leave out the commit name, git grep will search any of the
|
||||||
|
files it manages in your current directory. So
|
||||||
|
|
||||||
|
-------------------------------------
|
||||||
|
$ git grep "hello"
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
is a quick way to search just the files that are tracked by git.
|
||||||
|
|
||||||
|
Many git commands also take sets of commits, which can be specified
|
||||||
|
in a number of ways. Here are some examples with git log:
|
||||||
|
|
||||||
|
-------------------------------------
|
||||||
|
$ git log v2.5..v2.6 # commits between v2.5 and v2.6
|
||||||
|
$ git log v2.5.. # commits since v2.5
|
||||||
|
$ git log --since="2 weeks ago" # commits from the last 2 weeks
|
||||||
|
$ git log v2.5.. Makefile # commits since v2.5 which modify
|
||||||
|
# Makefile
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
You can also give git log a "range" of commits where the first is not
|
||||||
|
necessarily an ancestor of the second; for example, if the tips of
|
||||||
|
the branches "stable-release" and "master" diverged from a common
|
||||||
|
commit some time ago, then
|
||||||
|
|
||||||
|
-------------------------------------
|
||||||
|
$ git log stable..experimental
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
will list commits made in the experimental branch but not in the
|
||||||
|
stable branch, while
|
||||||
|
|
||||||
|
-------------------------------------
|
||||||
|
$ git log experimental..stable
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
will show the list of commits made on the stable branch but not
|
||||||
|
the experimental branch.
|
||||||
|
|
||||||
|
The "git log" command has a weakness: it must present commits in a
|
||||||
|
list. When the history has lines of development that diverged and
|
||||||
|
then merged back together, the order in which "git log" presents
|
||||||
|
those commits is meaningless.
|
||||||
|
|
||||||
|
Most projects with multiple contributors (such as the linux kernel,
|
||||||
|
or git itself) have frequent merges, and gitk does a better job of
|
||||||
|
visualizing their history. For example,
|
||||||
|
|
||||||
|
-------------------------------------
|
||||||
|
$ gitk --since="2 weeks ago" drivers/
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
allows you to browse any commits from the last 2 weeks of commits
|
||||||
|
that modified files under the "drivers" directory.
|
||||||
|
|
||||||
|
Finally, most commands that take filenames will optionally allow you
|
||||||
|
to precede any filename by a commit, to specify a particular version
|
||||||
|
fo the file:
|
||||||
|
|
||||||
|
-------------------------------------
|
||||||
|
$ git diff v2.5:Makefile HEAD:Makefile.in
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
Next Steps
|
Next Steps
|
||||||
----------
|
----------
|
||||||
|
|
||||||
Some good commands to explore next:
|
This tutorial should be enough to perform basic distributed revision
|
||||||
|
control for your projects. However, to fully understand the depth
|
||||||
|
and power of git you need to understand two simple ideas on which it
|
||||||
|
is based:
|
||||||
|
|
||||||
* gitlink:git-diff[1]: This flexible command does much more than
|
* The object database is the rather elegant system used to
|
||||||
we've seen in the few examples above.
|
store the history of your project--files, directories, and
|
||||||
|
commits.
|
||||||
|
|
||||||
|
* The index file is a cache of the state of a directory tree,
|
||||||
|
used to create commits, check out working directories, and
|
||||||
|
hold the various trees involved in a merge.
|
||||||
|
|
||||||
|
link:tutorial-2.html[Part two of this tutorial] explains the object
|
||||||
|
database, the index file, and a few other odds and ends that you'll
|
||||||
|
need to make the most of git.
|
||||||
|
|
||||||
|
If you don't want to consider with that right away, a few other
|
||||||
|
digressions that may be interesting at this point are:
|
||||||
|
|
||||||
* gitlink:git-format-patch[1], gitlink:git-am[1]: These convert
|
* gitlink:git-format-patch[1], gitlink:git-am[1]: These convert
|
||||||
series of git commits into emailed patches, and vice versa,
|
series of git commits into emailed patches, and vice versa,
|
||||||
@ -397,8 +474,6 @@ Some good commands to explore next:
|
|||||||
smart enough to perform a close-to-optimal search even in the
|
smart enough to perform a close-to-optimal search even in the
|
||||||
case of complex non-linear history with lots of merged branches.
|
case of complex non-linear history with lots of merged branches.
|
||||||
|
|
||||||
Other good starting points include link:everyday.html[Everday GIT
|
* link:everyday.html[Everday GIT with 20 Commands Or So]
|
||||||
with 20 Commands Or So] and link:cvs-migration.html[git for CVS
|
|
||||||
users]. Also, link:core-tutorial.html[A short git tutorial] gives an
|
* link:cvs-migration.html[git for CVS users].
|
||||||
introduction to lower-level git commands for advanced users and
|
|
||||||
developers.
|
|
||||||
|
@ -5,7 +5,7 @@ DEF_VER=v1.3.GIT
|
|||||||
|
|
||||||
# First try git-describe, then see if there is a version file
|
# First try git-describe, then see if there is a version file
|
||||||
# (included in release tarballs), then default
|
# (included in release tarballs), then default
|
||||||
if VN=$(git-describe --abbrev=4 HEAD 2>/dev/null); then
|
if VN=$(git describe --abbrev=4 HEAD 2>/dev/null); then
|
||||||
VN=$(echo "$VN" | sed -e 's/-/./g');
|
VN=$(echo "$VN" | sed -e 's/-/./g');
|
||||||
elif test -f version
|
elif test -f version
|
||||||
then
|
then
|
||||||
@ -16,7 +16,7 @@ fi
|
|||||||
|
|
||||||
VN=$(expr "$VN" : v*'\(.*\)')
|
VN=$(expr "$VN" : v*'\(.*\)')
|
||||||
|
|
||||||
dirty=$(sh -c 'git-diff-index --name-only HEAD' 2>/dev/null) || dirty=
|
dirty=$(sh -c 'git diff-index --name-only HEAD' 2>/dev/null) || dirty=
|
||||||
case "$dirty" in
|
case "$dirty" in
|
||||||
'')
|
'')
|
||||||
;;
|
;;
|
||||||
|
79
Makefile
79
Makefile
@ -116,7 +116,7 @@ SCRIPT_SH = \
|
|||||||
git-bisect.sh git-branch.sh git-checkout.sh \
|
git-bisect.sh git-branch.sh git-checkout.sh \
|
||||||
git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
|
git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
|
||||||
git-fetch.sh \
|
git-fetch.sh \
|
||||||
git-format-patch.sh git-ls-remote.sh \
|
git-ls-remote.sh \
|
||||||
git-merge-one-file.sh git-parse-remote.sh \
|
git-merge-one-file.sh git-parse-remote.sh \
|
||||||
git-prune.sh git-pull.sh git-rebase.sh \
|
git-prune.sh git-pull.sh git-rebase.sh \
|
||||||
git-repack.sh git-request-pull.sh git-reset.sh \
|
git-repack.sh git-request-pull.sh git-reset.sh \
|
||||||
@ -149,19 +149,16 @@ SIMPLE_PROGRAMS = \
|
|||||||
|
|
||||||
# ... and all the rest that could be moved out of bindir to gitexecdir
|
# ... and all the rest that could be moved out of bindir to gitexecdir
|
||||||
PROGRAMS = \
|
PROGRAMS = \
|
||||||
git-apply$X git-cat-file$X \
|
git-checkout-index$X git-clone-pack$X \
|
||||||
git-checkout-index$X git-clone-pack$X git-commit-tree$X \
|
git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \
|
||||||
git-convert-objects$X git-diff-files$X \
|
|
||||||
git-diff-index$X git-diff-stages$X \
|
|
||||||
git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \
|
|
||||||
git-hash-object$X git-index-pack$X git-local-fetch$X \
|
git-hash-object$X git-index-pack$X git-local-fetch$X \
|
||||||
git-ls-files$X git-ls-tree$X git-mailinfo$X git-merge-base$X \
|
git-mailinfo$X git-merge-base$X \
|
||||||
git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \
|
git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \
|
||||||
git-peek-remote$X git-prune-packed$X git-read-tree$X \
|
git-peek-remote$X git-prune-packed$X \
|
||||||
git-receive-pack$X git-rev-parse$X \
|
git-receive-pack$X git-rev-parse$X \
|
||||||
git-send-pack$X git-show-branch$X git-shell$X \
|
git-send-pack$X git-shell$X \
|
||||||
git-show-index$X git-ssh-fetch$X \
|
git-show-index$X git-ssh-fetch$X \
|
||||||
git-ssh-upload$X git-tar-tree$X git-unpack-file$X \
|
git-ssh-upload$X git-unpack-file$X \
|
||||||
git-unpack-objects$X git-update-index$X git-update-server-info$X \
|
git-unpack-objects$X git-update-index$X git-update-server-info$X \
|
||||||
git-upload-pack$X git-verify-pack$X git-write-tree$X \
|
git-upload-pack$X git-verify-pack$X git-write-tree$X \
|
||||||
git-update-ref$X git-symbolic-ref$X \
|
git-update-ref$X git-symbolic-ref$X \
|
||||||
@ -172,7 +169,11 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X \
|
|||||||
git-count-objects$X git-diff$X git-push$X \
|
git-count-objects$X git-diff$X git-push$X \
|
||||||
git-grep$X git-add$X git-rm$X git-rev-list$X \
|
git-grep$X git-add$X git-rm$X git-rev-list$X \
|
||||||
git-check-ref-format$X \
|
git-check-ref-format$X \
|
||||||
git-init-db$X
|
git-init-db$X git-tar-tree$X git-upload-tar$X git-format-patch$X \
|
||||||
|
git-ls-files$X git-ls-tree$X \
|
||||||
|
git-read-tree$X git-commit-tree$X \
|
||||||
|
git-apply$X git-show-branch$X git-diff-files$X \
|
||||||
|
git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X
|
||||||
|
|
||||||
# what 'all' will build and 'install' will install, in gitexecdir
|
# what 'all' will build and 'install' will install, in gitexecdir
|
||||||
ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
|
ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
|
||||||
@ -221,7 +222,13 @@ LIB_OBJS = \
|
|||||||
BUILTIN_OBJS = \
|
BUILTIN_OBJS = \
|
||||||
builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
|
builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
|
||||||
builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \
|
builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \
|
||||||
builtin-rm.o builtin-init-db.o
|
builtin-rm.o builtin-init-db.o \
|
||||||
|
builtin-tar-tree.o builtin-upload-tar.o \
|
||||||
|
builtin-ls-files.o builtin-ls-tree.o \
|
||||||
|
builtin-read-tree.o builtin-commit-tree.o \
|
||||||
|
builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
|
||||||
|
builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
|
||||||
|
builtin-cat-file.o
|
||||||
|
|
||||||
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
|
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
|
||||||
LIBS = $(GITLIBS) -lz
|
LIBS = $(GITLIBS) -lz
|
||||||
@ -421,6 +428,9 @@ else
|
|||||||
ALL_CFLAGS += -Dsockaddr_storage=sockaddr_in6
|
ALL_CFLAGS += -Dsockaddr_storage=sockaddr_in6
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
ifdef NO_INET_NTOP
|
||||||
|
LIB_OBJS += compat/inet_ntop.o
|
||||||
|
endif
|
||||||
|
|
||||||
ifdef NO_ICONV
|
ifdef NO_ICONV
|
||||||
ALL_CFLAGS += -DNO_ICONV
|
ALL_CFLAGS += -DNO_ICONV
|
||||||
@ -486,37 +496,43 @@ $(BUILT_INS): git$X
|
|||||||
rm -f $@ && ln git$X $@
|
rm -f $@ && ln git$X $@
|
||||||
|
|
||||||
common-cmds.h: Documentation/git-*.txt
|
common-cmds.h: Documentation/git-*.txt
|
||||||
./generate-cmdlist.sh > $@
|
./generate-cmdlist.sh > $@+
|
||||||
|
mv $@+ $@
|
||||||
|
|
||||||
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
|
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
|
||||||
rm -f $@
|
rm -f $@ $@+
|
||||||
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
|
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
|
||||||
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
|
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
|
||||||
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
|
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
|
||||||
-e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
|
-e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
|
||||||
$@.sh >$@
|
$@.sh >$@+
|
||||||
chmod +x $@
|
chmod +x $@+
|
||||||
|
mv $@+ $@
|
||||||
|
|
||||||
$(patsubst %.perl,%,$(SCRIPT_PERL)) : % : %.perl
|
$(patsubst %.perl,%,$(SCRIPT_PERL)) : % : %.perl
|
||||||
rm -f $@
|
rm -f $@ $@+
|
||||||
sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
|
sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
|
||||||
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
|
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
|
||||||
$@.perl >$@
|
$@.perl >$@+
|
||||||
chmod +x $@
|
chmod +x $@+
|
||||||
|
mv $@+ $@
|
||||||
|
|
||||||
$(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
|
$(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
|
||||||
rm -f $@
|
rm -f $@ $@+
|
||||||
sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
|
sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
|
||||||
-e 's|@@GIT_PYTHON_PATH@@|$(GIT_PYTHON_DIR_SQ)|g' \
|
-e 's|@@GIT_PYTHON_PATH@@|$(GIT_PYTHON_DIR_SQ)|g' \
|
||||||
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
|
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
|
||||||
$@.py >$@
|
$@.py >$@+
|
||||||
chmod +x $@
|
chmod +x $@+
|
||||||
|
mv $@+ $@
|
||||||
|
|
||||||
git-cherry-pick: git-revert
|
git-cherry-pick: git-revert
|
||||||
cp $< $@
|
cp $< $@+
|
||||||
|
mv $@+ $@
|
||||||
|
|
||||||
git-status: git-commit
|
git-status: git-commit
|
||||||
cp $< $@
|
cp $< $@+
|
||||||
|
mv $@+ $@
|
||||||
|
|
||||||
# These can record GIT_VERSION
|
# These can record GIT_VERSION
|
||||||
git$X git.spec \
|
git$X git.spec \
|
||||||
@ -628,7 +644,14 @@ install: all
|
|||||||
$(MAKE) -C templates install
|
$(MAKE) -C templates install
|
||||||
$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
|
$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
|
||||||
$(INSTALL) $(PYMODULES) '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
|
$(INSTALL) $(PYMODULES) '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
|
||||||
$(foreach p,$(BUILT_INS), rm -f '$(DESTDIR_SQ)$(bindir_SQ)/$p' && ln '$(DESTDIR_SQ)$(bindir_SQ)/git$X' '$(DESTDIR_SQ)$(bindir_SQ)/$p' ;)
|
if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \
|
||||||
|
then \
|
||||||
|
ln -f '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \
|
||||||
|
'$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' || \
|
||||||
|
cp '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \
|
||||||
|
'$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X'; \
|
||||||
|
fi
|
||||||
|
$(foreach p,$(BUILT_INS), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;)
|
||||||
|
|
||||||
install-doc:
|
install-doc:
|
||||||
$(MAKE) -C Documentation install
|
$(MAKE) -C Documentation install
|
||||||
@ -639,7 +662,8 @@ install-doc:
|
|||||||
### Maintainer's dist rules
|
### Maintainer's dist rules
|
||||||
|
|
||||||
git.spec: git.spec.in
|
git.spec: git.spec.in
|
||||||
sed -e 's/@@VERSION@@/$(GIT_VERSION)/g' < $< > $@
|
sed -e 's/@@VERSION@@/$(GIT_VERSION)/g' < $< > $@+
|
||||||
|
mv $@+ $@
|
||||||
|
|
||||||
GIT_TARNAME=git-$(GIT_VERSION)
|
GIT_TARNAME=git-$(GIT_VERSION)
|
||||||
dist: git.spec git-tar-tree
|
dist: git.spec git-tar-tree
|
||||||
@ -666,7 +690,7 @@ dist-doc:
|
|||||||
:
|
:
|
||||||
rm -fr .doc-tmp-dir
|
rm -fr .doc-tmp-dir
|
||||||
mkdir .doc-tmp-dir .doc-tmp-dir/man1 .doc-tmp-dir/man7
|
mkdir .doc-tmp-dir .doc-tmp-dir/man1 .doc-tmp-dir/man7
|
||||||
$(MAKE) -C Documentation DESTDIR=. \
|
$(MAKE) -C Documentation DESTDIR=./ \
|
||||||
man1=../.doc-tmp-dir/man1 \
|
man1=../.doc-tmp-dir/man1 \
|
||||||
man7=../.doc-tmp-dir/man7 \
|
man7=../.doc-tmp-dir/man7 \
|
||||||
install
|
install
|
||||||
@ -710,4 +734,3 @@ check-docs::
|
|||||||
*) echo "no link: $$v";; \
|
*) echo "no link: $$v";; \
|
||||||
esac ; \
|
esac ; \
|
||||||
done | sort
|
done | sort
|
||||||
|
|
||||||
|
4
blame.c
4
blame.c
@ -149,7 +149,7 @@ static void free_patch(struct patch *p)
|
|||||||
free(p);
|
free(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_blob_sha1_internal(unsigned char *sha1, const char *base,
|
static int get_blob_sha1_internal(const unsigned char *sha1, const char *base,
|
||||||
int baselen, const char *pathname,
|
int baselen, const char *pathname,
|
||||||
unsigned mode, int stage);
|
unsigned mode, int stage);
|
||||||
|
|
||||||
@ -178,7 +178,7 @@ static int get_blob_sha1(struct tree *t, const char *pathname,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_blob_sha1_internal(unsigned char *sha1, const char *base,
|
static int get_blob_sha1_internal(const unsigned char *sha1, const char *base,
|
||||||
int baselen, const char *pathname,
|
int baselen, const char *pathname,
|
||||||
unsigned mode, int stage)
|
unsigned mode, int stage)
|
||||||
{
|
{
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "quote.h"
|
#include "quote.h"
|
||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
#include "delta.h"
|
#include "delta.h"
|
||||||
|
#include "builtin.h"
|
||||||
|
|
||||||
// --check turns on checking that the working tree matches the
|
// --check turns on checking that the working tree matches the
|
||||||
// files that are being modified, but doesn't apply the patch
|
// files that are being modified, but doesn't apply the patch
|
||||||
@ -2154,7 +2155,7 @@ static int git_apply_config(const char *var, const char *value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int cmd_apply(int argc, const char **argv, char **envp)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int read_stdin = 1;
|
int read_stdin = 1;
|
@ -7,6 +7,7 @@
|
|||||||
#include "exec_cmd.h"
|
#include "exec_cmd.h"
|
||||||
#include "tag.h"
|
#include "tag.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
|
#include "builtin.h"
|
||||||
|
|
||||||
static void flush_buffer(const char *buf, unsigned long size)
|
static void flush_buffer(const char *buf, unsigned long size)
|
||||||
{
|
{
|
||||||
@ -93,7 +94,7 @@ static int pprint_tag(const unsigned char *sha1, const char *buf, unsigned long
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int cmd_cat_file(int argc, const char **argv, char **envp)
|
||||||
{
|
{
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
char type[20];
|
char type[20];
|
||||||
@ -140,7 +141,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
/* custom pretty-print here */
|
/* custom pretty-print here */
|
||||||
if (!strcmp(type, tree_type))
|
if (!strcmp(type, tree_type))
|
||||||
return execl_git_cmd("ls-tree", argv[2], NULL);
|
return cmd_ls_tree(2, argv + 1, NULL);
|
||||||
|
|
||||||
buf = read_sha1_file(sha1, type, &size);
|
buf = read_sha1_file(sha1, type, &size);
|
||||||
if (!buf)
|
if (!buf)
|
@ -6,6 +6,7 @@
|
|||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
|
#include "builtin.h"
|
||||||
|
|
||||||
#define BLOCKING (1ul << 14)
|
#define BLOCKING (1ul << 14)
|
||||||
|
|
||||||
@ -76,7 +77,7 @@ static int new_parent(int idx)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int cmd_commit_tree(int argc, const char **argv, char **envp)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int parents = 0;
|
int parents = 0;
|
||||||
@ -98,7 +99,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
check_valid(tree_sha1, tree_type);
|
check_valid(tree_sha1, tree_type);
|
||||||
for (i = 2; i < argc; i += 2) {
|
for (i = 2; i < argc; i += 2) {
|
||||||
char *a, *b;
|
const char *a, *b;
|
||||||
a = argv[i]; b = argv[i+1];
|
a = argv[i]; b = argv[i+1];
|
||||||
if (!b || strcmp(a, "-p"))
|
if (!b || strcmp(a, "-p"))
|
||||||
usage(commit_tree_usage);
|
usage(commit_tree_usage);
|
@ -7,12 +7,13 @@
|
|||||||
#include "diff.h"
|
#include "diff.h"
|
||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
#include "revision.h"
|
#include "revision.h"
|
||||||
|
#include "builtin.h"
|
||||||
|
|
||||||
static const char diff_files_usage[] =
|
static const char diff_files_usage[] =
|
||||||
"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
|
"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
|
||||||
COMMON_DIFF_OPTIONS_HELP;
|
COMMON_DIFF_OPTIONS_HELP;
|
||||||
|
|
||||||
int main(int argc, const char **argv)
|
int cmd_diff_files(int argc, const char **argv, char **envp)
|
||||||
{
|
{
|
||||||
struct rev_info rev;
|
struct rev_info rev;
|
||||||
int silent = 0;
|
int silent = 0;
|
@ -2,13 +2,14 @@
|
|||||||
#include "diff.h"
|
#include "diff.h"
|
||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
#include "revision.h"
|
#include "revision.h"
|
||||||
|
#include "builtin.h"
|
||||||
|
|
||||||
static const char diff_cache_usage[] =
|
static const char diff_cache_usage[] =
|
||||||
"git-diff-index [-m] [--cached] "
|
"git-diff-index [-m] [--cached] "
|
||||||
"[<common diff options>] <tree-ish> [<path>...]"
|
"[<common diff options>] <tree-ish> [<path>...]"
|
||||||
COMMON_DIFF_OPTIONS_HELP;
|
COMMON_DIFF_OPTIONS_HELP;
|
||||||
|
|
||||||
int main(int argc, const char **argv)
|
int cmd_diff_index(int argc, const char **argv, char **envp)
|
||||||
{
|
{
|
||||||
struct rev_info rev;
|
struct rev_info rev;
|
||||||
int cached = 0;
|
int cached = 0;
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "diff.h"
|
#include "diff.h"
|
||||||
|
#include "builtin.h"
|
||||||
|
|
||||||
static struct diff_options diff_options;
|
static struct diff_options diff_options;
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ static void diff_stages(int stage1, int stage2, const char **pathspec)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int ac, const char **av)
|
int cmd_diff_stages(int ac, const char **av, char **envp)
|
||||||
{
|
{
|
||||||
int stage1, stage2;
|
int stage1, stage2;
|
||||||
const char *prefix = setup_git_directory();
|
const char *prefix = setup_git_directory();
|
@ -2,6 +2,7 @@
|
|||||||
#include "diff.h"
|
#include "diff.h"
|
||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
#include "log-tree.h"
|
#include "log-tree.h"
|
||||||
|
#include "builtin.h"
|
||||||
|
|
||||||
static struct rev_info log_tree_opt;
|
static struct rev_info log_tree_opt;
|
||||||
|
|
||||||
@ -58,7 +59,7 @@ static const char diff_tree_usage[] =
|
|||||||
" --root include the initial commit as diff against /dev/null\n"
|
" --root include the initial commit as diff against /dev/null\n"
|
||||||
COMMON_DIFF_OPTIONS_HELP;
|
COMMON_DIFF_OPTIONS_HELP;
|
||||||
|
|
||||||
int main(int argc, const char **argv)
|
int cmd_diff_tree(int argc, const char **argv, char **envp)
|
||||||
{
|
{
|
||||||
int nr_sha1;
|
int nr_sha1;
|
||||||
char line[1000];
|
char line[1000];
|
@ -233,7 +233,7 @@ static int builtin_diff_combined(struct rev_info *revs,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_head(struct rev_info *revs)
|
void add_head(struct rev_info *revs)
|
||||||
{
|
{
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
struct object *obj;
|
struct object *obj;
|
||||||
|
@ -518,7 +518,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
|
|||||||
argc = nr;
|
argc = nr;
|
||||||
for (i = 0; i < active_nr; i++) {
|
for (i = 0; i < active_nr; i++) {
|
||||||
struct cache_entry *ce = active_cache[i];
|
struct cache_entry *ce = active_cache[i];
|
||||||
const char *name;
|
char *name;
|
||||||
if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode)))
|
if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode)))
|
||||||
continue;
|
continue;
|
||||||
if (!pathspec_matches(paths, ce->name))
|
if (!pathspec_matches(paths, ce->name))
|
||||||
|
191
builtin-log.c
191
builtin-log.c
@ -9,6 +9,10 @@
|
|||||||
#include "diff.h"
|
#include "diff.h"
|
||||||
#include "revision.h"
|
#include "revision.h"
|
||||||
#include "log-tree.h"
|
#include "log-tree.h"
|
||||||
|
#include "builtin.h"
|
||||||
|
|
||||||
|
/* this is in builtin-diff.c */
|
||||||
|
void add_head(struct rev_info *revs);
|
||||||
|
|
||||||
static int cmd_log_wc(int argc, const char **argv, char **envp,
|
static int cmd_log_wc(int argc, const char **argv, char **envp,
|
||||||
struct rev_info *rev)
|
struct rev_info *rev)
|
||||||
@ -74,3 +78,190 @@ int cmd_log(int argc, const char **argv, char **envp)
|
|||||||
rev.diffopt.recursive = 1;
|
rev.diffopt.recursive = 1;
|
||||||
return cmd_log_wc(argc, argv, envp, &rev);
|
return cmd_log_wc(argc, argv, envp, &rev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int istitlechar(char c)
|
||||||
|
{
|
||||||
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||||
|
(c >= '0' && c <= '9') || c == '.' || c == '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
static FILE *realstdout = NULL;
|
||||||
|
static char *output_directory = NULL;
|
||||||
|
|
||||||
|
static void reopen_stdout(struct commit *commit, int nr, int keep_subject)
|
||||||
|
{
|
||||||
|
char filename[1024];
|
||||||
|
char *sol;
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
if (output_directory) {
|
||||||
|
strncpy(filename, output_directory, 1010);
|
||||||
|
len = strlen(filename);
|
||||||
|
if (filename[len - 1] != '/')
|
||||||
|
filename[len++] = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(filename + len, "%04d", nr);
|
||||||
|
len = strlen(filename);
|
||||||
|
|
||||||
|
sol = strstr(commit->buffer, "\n\n");
|
||||||
|
if (sol) {
|
||||||
|
int j, space = 1;
|
||||||
|
|
||||||
|
sol += 2;
|
||||||
|
/* strip [PATCH] or [PATCH blabla] */
|
||||||
|
if (!keep_subject && !strncmp(sol, "[PATCH", 6)) {
|
||||||
|
char *eos = strchr(sol + 6, ']');
|
||||||
|
if (eos) {
|
||||||
|
while (isspace(*eos))
|
||||||
|
eos++;
|
||||||
|
sol = eos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; len < 1024 - 6 && sol[j] && sol[j] != '\n'; j++) {
|
||||||
|
if (istitlechar(sol[j])) {
|
||||||
|
if (space) {
|
||||||
|
filename[len++] = '-';
|
||||||
|
space = 0;
|
||||||
|
}
|
||||||
|
filename[len++] = sol[j];
|
||||||
|
if (sol[j] == '.')
|
||||||
|
while (sol[j + 1] == '.')
|
||||||
|
j++;
|
||||||
|
} else
|
||||||
|
space = 1;
|
||||||
|
}
|
||||||
|
while (filename[len - 1] == '.' || filename[len - 1] == '-')
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
strcpy(filename + len, ".txt");
|
||||||
|
fprintf(realstdout, "%s\n", filename);
|
||||||
|
freopen(filename, "w", stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmd_format_patch(int argc, const char **argv, char **envp)
|
||||||
|
{
|
||||||
|
struct commit *commit;
|
||||||
|
struct commit **list = NULL;
|
||||||
|
struct rev_info rev;
|
||||||
|
int nr = 0, total, i, j;
|
||||||
|
int use_stdout = 0;
|
||||||
|
int numbered = 0;
|
||||||
|
int start_number = -1;
|
||||||
|
int keep_subject = 0;
|
||||||
|
|
||||||
|
init_revisions(&rev);
|
||||||
|
rev.commit_format = CMIT_FMT_EMAIL;
|
||||||
|
rev.verbose_header = 1;
|
||||||
|
rev.diff = 1;
|
||||||
|
rev.diffopt.with_raw = 0;
|
||||||
|
rev.diffopt.with_stat = 1;
|
||||||
|
rev.combine_merges = 0;
|
||||||
|
rev.ignore_merges = 1;
|
||||||
|
rev.diffopt.output_format = DIFF_FORMAT_PATCH;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse the arguments before setup_revisions(), or something
|
||||||
|
* like "git fmt-patch -o a123 HEAD^.." may fail; a123 is
|
||||||
|
* possibly a valid SHA1.
|
||||||
|
*/
|
||||||
|
for (i = 1, j = 1; i < argc; i++) {
|
||||||
|
if (!strcmp(argv[i], "--stdout"))
|
||||||
|
use_stdout = 1;
|
||||||
|
else if (!strcmp(argv[i], "-n") ||
|
||||||
|
!strcmp(argv[i], "--numbered"))
|
||||||
|
numbered = 1;
|
||||||
|
else if (!strncmp(argv[i], "--start-number=", 15))
|
||||||
|
start_number = strtol(argv[i] + 15, NULL, 10);
|
||||||
|
else if (!strcmp(argv[i], "--start-number")) {
|
||||||
|
i++;
|
||||||
|
if (i == argc)
|
||||||
|
die("Need a number for --start-number");
|
||||||
|
start_number = strtol(argv[i], NULL, 10);
|
||||||
|
} else if (!strcmp(argv[i], "-k") ||
|
||||||
|
!strcmp(argv[i], "--keep-subject")) {
|
||||||
|
keep_subject = 1;
|
||||||
|
rev.total = -1;
|
||||||
|
} else if (!strcmp(argv[i], "-o")) {
|
||||||
|
if (argc < 3)
|
||||||
|
die ("Which directory?");
|
||||||
|
if (mkdir(argv[i + 1], 0777) < 0 && errno != EEXIST)
|
||||||
|
die("Could not create directory %s",
|
||||||
|
argv[i + 1]);
|
||||||
|
output_directory = strdup(argv[i + 1]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (!strcmp(argv[i], "--attach"))
|
||||||
|
rev.mime_boundary = git_version_string;
|
||||||
|
else if (!strncmp(argv[i], "--attach=", 9))
|
||||||
|
rev.mime_boundary = argv[i] + 9;
|
||||||
|
else
|
||||||
|
argv[j++] = argv[i];
|
||||||
|
}
|
||||||
|
argc = j;
|
||||||
|
|
||||||
|
if (start_number < 0)
|
||||||
|
start_number = 1;
|
||||||
|
if (numbered && keep_subject)
|
||||||
|
die ("-n and -k are mutually exclusive.");
|
||||||
|
|
||||||
|
argc = setup_revisions(argc, argv, &rev, "HEAD");
|
||||||
|
if (argc > 1)
|
||||||
|
die ("unrecognized argument: %s", argv[1]);
|
||||||
|
|
||||||
|
if (rev.pending_objects && rev.pending_objects->next == NULL) {
|
||||||
|
rev.pending_objects->item->flags |= UNINTERESTING;
|
||||||
|
add_head(&rev);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!use_stdout)
|
||||||
|
realstdout = fdopen(dup(1), "w");
|
||||||
|
|
||||||
|
prepare_revision_walk(&rev);
|
||||||
|
while ((commit = get_revision(&rev)) != NULL) {
|
||||||
|
/* ignore merges */
|
||||||
|
if (commit->parents && commit->parents->next)
|
||||||
|
continue;
|
||||||
|
nr++;
|
||||||
|
list = realloc(list, nr * sizeof(list[0]));
|
||||||
|
list[nr - 1] = commit;
|
||||||
|
}
|
||||||
|
total = nr;
|
||||||
|
if (numbered)
|
||||||
|
rev.total = total + start_number - 1;
|
||||||
|
while (0 <= --nr) {
|
||||||
|
int shown;
|
||||||
|
commit = list[nr];
|
||||||
|
rev.nr = total - nr + (start_number - 1);
|
||||||
|
if (!use_stdout)
|
||||||
|
reopen_stdout(commit, rev.nr, keep_subject);
|
||||||
|
shown = log_tree_commit(&rev, commit);
|
||||||
|
free(commit->buffer);
|
||||||
|
commit->buffer = NULL;
|
||||||
|
|
||||||
|
/* We put one extra blank line between formatted
|
||||||
|
* patches and this flag is used by log-tree code
|
||||||
|
* to see if it needs to emit a LF before showing
|
||||||
|
* the log; when using one file per patch, we do
|
||||||
|
* not want the extra blank line.
|
||||||
|
*/
|
||||||
|
if (!use_stdout)
|
||||||
|
rev.shown_one = 0;
|
||||||
|
if (shown) {
|
||||||
|
if (rev.mime_boundary)
|
||||||
|
printf("\n--%s%s--\n\n\n",
|
||||||
|
mime_boundary_leader,
|
||||||
|
rev.mime_boundary);
|
||||||
|
else
|
||||||
|
printf("-- \n%s\n\n", git_version_string);
|
||||||
|
}
|
||||||
|
if (!use_stdout)
|
||||||
|
fclose(stdout);
|
||||||
|
}
|
||||||
|
if (output_directory)
|
||||||
|
free(output_directory);
|
||||||
|
free(list);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "quote.h"
|
#include "quote.h"
|
||||||
#include "dir.h"
|
#include "dir.h"
|
||||||
|
#include "builtin.h"
|
||||||
|
|
||||||
static int abbrev = 0;
|
static int abbrev = 0;
|
||||||
static int show_deleted = 0;
|
static int show_deleted = 0;
|
||||||
@ -321,7 +322,7 @@ static const char ls_files_usage[] =
|
|||||||
"[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] "
|
"[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] "
|
||||||
"[--] [<file>]*";
|
"[--] [<file>]*";
|
||||||
|
|
||||||
int main(int argc, const char **argv)
|
int cmd_ls_files(int argc, const char **argv, char** envp)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int exc_given = 0;
|
int exc_given = 0;
|
@ -7,6 +7,7 @@
|
|||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
#include "quote.h"
|
#include "quote.h"
|
||||||
|
#include "builtin.h"
|
||||||
|
|
||||||
static int line_termination = '\n';
|
static int line_termination = '\n';
|
||||||
#define LS_RECURSIVE 1
|
#define LS_RECURSIVE 1
|
||||||
@ -15,7 +16,7 @@ static int line_termination = '\n';
|
|||||||
#define LS_NAME_ONLY 8
|
#define LS_NAME_ONLY 8
|
||||||
static int abbrev = 0;
|
static int abbrev = 0;
|
||||||
static int ls_options = 0;
|
static int ls_options = 0;
|
||||||
const char **pathspec;
|
static const char **pathspec;
|
||||||
static int chomp_prefix = 0;
|
static int chomp_prefix = 0;
|
||||||
static const char *prefix;
|
static const char *prefix;
|
||||||
|
|
||||||
@ -52,7 +53,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int show_tree(unsigned char *sha1, const char *base, int baselen,
|
static int show_tree(const unsigned char *sha1, const char *base, int baselen,
|
||||||
const char *pathname, unsigned mode, int stage)
|
const char *pathname, unsigned mode, int stage)
|
||||||
{
|
{
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
@ -84,7 +85,7 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen,
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char **argv)
|
int cmd_ls_tree(int argc, const char **argv, char **envp)
|
||||||
{
|
{
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
struct tree *tree;
|
struct tree *tree;
|
@ -10,8 +10,10 @@
|
|||||||
#include "object.h"
|
#include "object.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
#include "cache-tree.h"
|
#include "cache-tree.h"
|
||||||
|
#include "tree-walk.h"
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include "builtin.h"
|
||||||
|
|
||||||
static int reset = 0;
|
static int reset = 0;
|
||||||
static int merge = 0;
|
static int merge = 0;
|
||||||
@ -38,7 +40,7 @@ static struct tree_entry_list df_conflict_list = {
|
|||||||
|
|
||||||
typedef int (*merge_fn_t)(struct cache_entry **src);
|
typedef int (*merge_fn_t)(struct cache_entry **src);
|
||||||
|
|
||||||
static int entcmp(char *name1, int dir1, char *name2, int dir2)
|
static int entcmp(const char *name1, int dir1, const char *name2, int dir2)
|
||||||
{
|
{
|
||||||
int len1 = strlen(name1);
|
int len1 = strlen(name1);
|
||||||
int len2 = strlen(name2);
|
int len2 = strlen(name2);
|
||||||
@ -66,7 +68,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
|
|||||||
int src_size = len + 1;
|
int src_size = len + 1;
|
||||||
do {
|
do {
|
||||||
int i;
|
int i;
|
||||||
char *first;
|
const char *first;
|
||||||
int firstdir = 0;
|
int firstdir = 0;
|
||||||
int pathlen;
|
int pathlen;
|
||||||
unsigned ce_size;
|
unsigned ce_size;
|
||||||
@ -160,9 +162,10 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (posns[i]->directory) {
|
if (posns[i]->directory) {
|
||||||
|
struct tree *tree = lookup_tree(posns[i]->sha1);
|
||||||
any_dirs = 1;
|
any_dirs = 1;
|
||||||
parse_tree(posns[i]->item.tree);
|
parse_tree(tree);
|
||||||
subposns[i] = posns[i]->item.tree->entries;
|
subposns[i] = create_tree_entry_list(tree);
|
||||||
posns[i] = posns[i]->next;
|
posns[i] = posns[i]->next;
|
||||||
src[i + merge] = &df_conflict_entry;
|
src[i + merge] = &df_conflict_entry;
|
||||||
continue;
|
continue;
|
||||||
@ -186,7 +189,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
|
|||||||
|
|
||||||
any_files = 1;
|
any_files = 1;
|
||||||
|
|
||||||
memcpy(ce->sha1, posns[i]->item.any->sha1, 20);
|
memcpy(ce->sha1, posns[i]->sha1, 20);
|
||||||
src[i + merge] = ce;
|
src[i + merge] = ce;
|
||||||
subposns[i] = &df_conflict_list;
|
subposns[i] = &df_conflict_list;
|
||||||
posns[i] = posns[i]->next;
|
posns[i] = posns[i]->next;
|
||||||
@ -367,7 +370,7 @@ static int unpack_trees(merge_fn_t fn)
|
|||||||
if (len) {
|
if (len) {
|
||||||
posns = xmalloc(len * sizeof(struct tree_entry_list *));
|
posns = xmalloc(len * sizeof(struct tree_entry_list *));
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
posns[i] = ((struct tree *) posn->item)->entries;
|
posns[i] = create_tree_entry_list((struct tree *) posn->item);
|
||||||
posn = posn->next;
|
posn = posn->next;
|
||||||
}
|
}
|
||||||
if (unpack_trees_rec(posns, len, "", fn, &indpos))
|
if (unpack_trees_rec(posns, len, "", fn, &indpos))
|
||||||
@ -773,19 +776,31 @@ static int read_cache_unmerged(void)
|
|||||||
|
|
||||||
static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
|
static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
|
||||||
{
|
{
|
||||||
struct tree_entry_list *ent;
|
struct tree_desc desc;
|
||||||
int cnt;
|
int cnt = 0;
|
||||||
|
|
||||||
memcpy(it->sha1, tree->object.sha1, 20);
|
memcpy(it->sha1, tree->object.sha1, 20);
|
||||||
for (cnt = 0, ent = tree->entries; ent; ent = ent->next) {
|
desc.buf = tree->buffer;
|
||||||
if (!ent->directory)
|
desc.size = tree->size;
|
||||||
|
|
||||||
|
while (desc.size) {
|
||||||
|
unsigned mode;
|
||||||
|
const char *name;
|
||||||
|
const unsigned char *sha1;
|
||||||
|
|
||||||
|
sha1 = tree_entry_extract(&desc, &name, &mode);
|
||||||
|
update_tree_entry(&desc);
|
||||||
|
|
||||||
|
if (!S_ISDIR(mode))
|
||||||
cnt++;
|
cnt++;
|
||||||
else {
|
else {
|
||||||
struct cache_tree_sub *sub;
|
struct cache_tree_sub *sub;
|
||||||
struct tree *subtree = (struct tree *)ent->item.tree;
|
struct tree *subtree;
|
||||||
|
|
||||||
|
subtree = lookup_tree(sha1);
|
||||||
if (!subtree->object.parsed)
|
if (!subtree->object.parsed)
|
||||||
parse_tree(subtree);
|
parse_tree(subtree);
|
||||||
sub = cache_tree_sub(it, ent->name);
|
sub = cache_tree_sub(it, name);
|
||||||
sub->cache_tree = cache_tree();
|
sub->cache_tree = cache_tree();
|
||||||
prime_cache_tree_rec(sub->cache_tree, subtree);
|
prime_cache_tree_rec(sub->cache_tree, subtree);
|
||||||
cnt += sub->cache_tree->entry_count;
|
cnt += sub->cache_tree->entry_count;
|
||||||
@ -808,7 +823,7 @@ static const char read_tree_usage[] = "git-read-tree (<sha> | -m [--aggressive]
|
|||||||
|
|
||||||
static struct cache_file cache_file;
|
static struct cache_file cache_file;
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int cmd_read_tree(int argc, const char **argv, char **envp)
|
||||||
{
|
{
|
||||||
int i, newfd, stage = 0;
|
int i, newfd, stage = 0;
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
@ -85,7 +85,7 @@ static void show_commit(struct commit *commit)
|
|||||||
static char pretty_header[16384];
|
static char pretty_header[16384];
|
||||||
pretty_print_commit(revs.commit_format, commit, ~0,
|
pretty_print_commit(revs.commit_format, commit, ~0,
|
||||||
pretty_header, sizeof(pretty_header),
|
pretty_header, sizeof(pretty_header),
|
||||||
revs.abbrev);
|
revs.abbrev, NULL, NULL);
|
||||||
printf("%s%c", pretty_header, hdr_termination);
|
printf("%s%c", pretty_header, hdr_termination);
|
||||||
}
|
}
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
@ -103,6 +103,7 @@ static struct object_list **process_blob(struct blob *blob,
|
|||||||
if (obj->flags & (UNINTERESTING | SEEN))
|
if (obj->flags & (UNINTERESTING | SEEN))
|
||||||
return p;
|
return p;
|
||||||
obj->flags |= SEEN;
|
obj->flags |= SEEN;
|
||||||
|
name = strdup(name);
|
||||||
return add_object(obj, p, path, name);
|
return add_object(obj, p, path, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +113,7 @@ static struct object_list **process_tree(struct tree *tree,
|
|||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
struct object *obj = &tree->object;
|
struct object *obj = &tree->object;
|
||||||
struct tree_entry_list *entry;
|
struct tree_desc desc;
|
||||||
struct name_path me;
|
struct name_path me;
|
||||||
|
|
||||||
if (!revs.tree_objects)
|
if (!revs.tree_objects)
|
||||||
@ -122,21 +123,30 @@ static struct object_list **process_tree(struct tree *tree,
|
|||||||
if (parse_tree(tree) < 0)
|
if (parse_tree(tree) < 0)
|
||||||
die("bad tree object %s", sha1_to_hex(obj->sha1));
|
die("bad tree object %s", sha1_to_hex(obj->sha1));
|
||||||
obj->flags |= SEEN;
|
obj->flags |= SEEN;
|
||||||
|
name = strdup(name);
|
||||||
p = add_object(obj, p, path, name);
|
p = add_object(obj, p, path, name);
|
||||||
me.up = path;
|
me.up = path;
|
||||||
me.elem = name;
|
me.elem = name;
|
||||||
me.elem_len = strlen(name);
|
me.elem_len = strlen(name);
|
||||||
entry = tree->entries;
|
|
||||||
tree->entries = NULL;
|
desc.buf = tree->buffer;
|
||||||
while (entry) {
|
desc.size = tree->size;
|
||||||
struct tree_entry_list *next = entry->next;
|
|
||||||
if (entry->directory)
|
while (desc.size) {
|
||||||
p = process_tree(entry->item.tree, p, &me, entry->name);
|
unsigned mode;
|
||||||
|
const char *name;
|
||||||
|
const unsigned char *sha1;
|
||||||
|
|
||||||
|
sha1 = tree_entry_extract(&desc, &name, &mode);
|
||||||
|
update_tree_entry(&desc);
|
||||||
|
|
||||||
|
if (S_ISDIR(mode))
|
||||||
|
p = process_tree(lookup_tree(sha1), p, &me, name);
|
||||||
else
|
else
|
||||||
p = process_blob(entry->item.blob, p, &me, entry->name);
|
p = process_blob(lookup_blob(sha1), p, &me, name);
|
||||||
free(entry);
|
|
||||||
entry = next;
|
|
||||||
}
|
}
|
||||||
|
free(tree->buffer);
|
||||||
|
tree->buffer = NULL;
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,13 +3,14 @@
|
|||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
#include "refs.h"
|
#include "refs.h"
|
||||||
|
#include "builtin.h"
|
||||||
|
|
||||||
static const char show_branch_usage[] =
|
static const char show_branch_usage[] =
|
||||||
"git-show-branch [--dense] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]";
|
"git-show-branch [--dense] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]";
|
||||||
|
|
||||||
static int default_num = 0;
|
static int default_num = 0;
|
||||||
static int default_alloc = 0;
|
static int default_alloc = 0;
|
||||||
static char **default_arg = NULL;
|
static const char **default_arg = NULL;
|
||||||
|
|
||||||
#define UNINTERESTING 01
|
#define UNINTERESTING 01
|
||||||
|
|
||||||
@ -259,7 +260,7 @@ static void show_one_commit(struct commit *commit, int no_name)
|
|||||||
struct commit_name *name = commit->object.util;
|
struct commit_name *name = commit->object.util;
|
||||||
if (commit->object.parsed)
|
if (commit->object.parsed)
|
||||||
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
|
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
|
||||||
pretty, sizeof(pretty), 0);
|
pretty, sizeof(pretty), 0, NULL, NULL);
|
||||||
else
|
else
|
||||||
strcpy(pretty, "(unavailable)");
|
strcpy(pretty, "(unavailable)");
|
||||||
if (!strncmp(pretty, "[PATCH] ", 8))
|
if (!strncmp(pretty, "[PATCH] ", 8))
|
||||||
@ -548,7 +549,7 @@ static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int ac, char **av)
|
int cmd_show_branch(int ac, const char **av, char **envp)
|
||||||
{
|
{
|
||||||
struct commit *rev[MAX_REVS], *commit;
|
struct commit *rev[MAX_REVS], *commit;
|
||||||
struct commit_list *list = NULL, *seen = NULL;
|
struct commit_list *list = NULL, *seen = NULL;
|
||||||
@ -581,7 +582,7 @@ int main(int ac, char **av)
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (1 < ac && av[1][0] == '-') {
|
while (1 < ac && av[1][0] == '-') {
|
||||||
char *arg = av[1];
|
const char *arg = av[1];
|
||||||
if (!strcmp(arg, "--")) {
|
if (!strcmp(arg, "--")) {
|
||||||
ac--; av++;
|
ac--; av++;
|
||||||
break;
|
break;
|
@ -7,11 +7,14 @@
|
|||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
#include "strbuf.h"
|
#include "strbuf.h"
|
||||||
#include "tar.h"
|
#include "tar.h"
|
||||||
|
#include "builtin.h"
|
||||||
|
#include "pkt-line.h"
|
||||||
|
|
||||||
#define RECORDSIZE (512)
|
#define RECORDSIZE (512)
|
||||||
#define BLOCKSIZE (RECORDSIZE * 20)
|
#define BLOCKSIZE (RECORDSIZE * 20)
|
||||||
|
|
||||||
static const char tar_tree_usage[] = "git-tar-tree <key> [basedir]";
|
static const char tar_tree_usage[] =
|
||||||
|
"git-tar-tree [--remote=<repo>] <ent> [basedir]";
|
||||||
|
|
||||||
static char block[BLOCKSIZE];
|
static char block[BLOCKSIZE];
|
||||||
static unsigned long offset;
|
static unsigned long offset;
|
||||||
@ -301,7 +304,7 @@ static void traverse_tree(struct tree_desc *tree, struct strbuf *path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
static int generate_tar(int argc, const char **argv, char** envp)
|
||||||
{
|
{
|
||||||
unsigned char sha1[20], tree_sha1[20];
|
unsigned char sha1[20], tree_sha1[20];
|
||||||
struct commit *commit;
|
struct commit *commit;
|
||||||
@ -348,3 +351,58 @@ int main(int argc, char **argv)
|
|||||||
free(current_path.buf);
|
free(current_path.buf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *exec = "git-upload-tar";
|
||||||
|
|
||||||
|
static int remote_tar(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
int fd[2], ret, len;
|
||||||
|
pid_t pid;
|
||||||
|
char buf[1024];
|
||||||
|
char *url;
|
||||||
|
|
||||||
|
if (argc < 3 || 4 < argc)
|
||||||
|
usage(tar_tree_usage);
|
||||||
|
|
||||||
|
/* --remote=<repo> */
|
||||||
|
url = strdup(argv[1]+9);
|
||||||
|
pid = git_connect(fd, url, exec);
|
||||||
|
if (pid < 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
packet_write(fd[1], "want %s\n", argv[2]);
|
||||||
|
if (argv[3])
|
||||||
|
packet_write(fd[1], "base %s\n", argv[3]);
|
||||||
|
packet_flush(fd[1]);
|
||||||
|
|
||||||
|
len = packet_read_line(fd[0], buf, sizeof(buf));
|
||||||
|
if (!len)
|
||||||
|
die("git-tar-tree: expected ACK/NAK, got EOF");
|
||||||
|
if (buf[len-1] == '\n')
|
||||||
|
buf[--len] = 0;
|
||||||
|
if (strcmp(buf, "ACK")) {
|
||||||
|
if (5 < len && !strncmp(buf, "NACK ", 5))
|
||||||
|
die("git-tar-tree: NACK %s", buf + 5);
|
||||||
|
die("git-tar-tree: protocol error");
|
||||||
|
}
|
||||||
|
/* expect a flush */
|
||||||
|
len = packet_read_line(fd[0], buf, sizeof(buf));
|
||||||
|
if (len)
|
||||||
|
die("git-tar-tree: expected a flush");
|
||||||
|
|
||||||
|
/* Now, start reading from fd[0] and spit it out to stdout */
|
||||||
|
ret = copy_fd(fd[0], 1);
|
||||||
|
close(fd[0]);
|
||||||
|
|
||||||
|
ret |= finish_connect(pid);
|
||||||
|
return !!ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmd_tar_tree(int argc, const char **argv, char **envp)
|
||||||
|
{
|
||||||
|
if (argc < 2)
|
||||||
|
usage(tar_tree_usage);
|
||||||
|
if (!strncmp("--remote=", argv[1], 9))
|
||||||
|
return remote_tar(argc, argv);
|
||||||
|
return generate_tar(argc, argv, envp);
|
||||||
|
}
|
74
builtin-upload-tar.c
Normal file
74
builtin-upload-tar.c
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2006 Junio C Hamano
|
||||||
|
*/
|
||||||
|
#include "cache.h"
|
||||||
|
#include "pkt-line.h"
|
||||||
|
#include "exec_cmd.h"
|
||||||
|
#include "builtin.h"
|
||||||
|
|
||||||
|
static const char upload_tar_usage[] = "git-upload-tar <repo>";
|
||||||
|
|
||||||
|
static int nak(const char *reason)
|
||||||
|
{
|
||||||
|
packet_write(1, "NACK %s\n", reason);
|
||||||
|
packet_flush(1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmd_upload_tar(int argc, const char **argv, char **envp)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
const char *dir = argv[1];
|
||||||
|
char buf[8192];
|
||||||
|
unsigned char sha1[20];
|
||||||
|
char *base = NULL;
|
||||||
|
char hex[41];
|
||||||
|
int ac;
|
||||||
|
const char *av[4];
|
||||||
|
|
||||||
|
if (argc != 2)
|
||||||
|
usage(upload_tar_usage);
|
||||||
|
if (strlen(dir) < sizeof(buf)-1)
|
||||||
|
strcpy(buf, dir); /* enter-repo smudges its argument */
|
||||||
|
else
|
||||||
|
packet_write(1, "NACK insanely long repository name %s\n", dir);
|
||||||
|
if (!enter_repo(buf, 0)) {
|
||||||
|
packet_write(1, "NACK not a git archive %s\n", dir);
|
||||||
|
packet_flush(1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = packet_read_line(0, buf, sizeof(buf));
|
||||||
|
if (len < 5 || strncmp("want ", buf, 5))
|
||||||
|
return nak("expected want");
|
||||||
|
if (buf[len-1] == '\n')
|
||||||
|
buf[--len] = 0;
|
||||||
|
if (get_sha1(buf + 5, sha1))
|
||||||
|
return nak("expected sha1");
|
||||||
|
strcpy(hex, sha1_to_hex(sha1));
|
||||||
|
|
||||||
|
len = packet_read_line(0, buf, sizeof(buf));
|
||||||
|
if (len) {
|
||||||
|
if (len < 5 || strncmp("base ", buf, 5))
|
||||||
|
return nak("expected (optional) base");
|
||||||
|
if (buf[len-1] == '\n')
|
||||||
|
buf[--len] = 0;
|
||||||
|
base = strdup(buf + 5);
|
||||||
|
len = packet_read_line(0, buf, sizeof(buf));
|
||||||
|
}
|
||||||
|
if (len)
|
||||||
|
return nak("expected flush");
|
||||||
|
|
||||||
|
packet_write(1, "ACK\n");
|
||||||
|
packet_flush(1);
|
||||||
|
|
||||||
|
ac = 0;
|
||||||
|
av[ac++] = "tar-tree";
|
||||||
|
av[ac++] = hex;
|
||||||
|
if (base)
|
||||||
|
av[ac++] = base;
|
||||||
|
av[ac++] = NULL;
|
||||||
|
execv_git_cmd(av);
|
||||||
|
/* should it return that is an error */
|
||||||
|
return 1;
|
||||||
|
}
|
14
builtin.h
14
builtin.h
@ -20,6 +20,7 @@ extern int cmd_whatchanged(int argc, const char **argv, char **envp);
|
|||||||
extern int cmd_show(int argc, const char **argv, char **envp);
|
extern int cmd_show(int argc, const char **argv, char **envp);
|
||||||
extern int cmd_log(int argc, const char **argv, char **envp);
|
extern int cmd_log(int argc, const char **argv, char **envp);
|
||||||
extern int cmd_diff(int argc, const char **argv, char **envp);
|
extern int cmd_diff(int argc, const char **argv, char **envp);
|
||||||
|
extern int cmd_format_patch(int argc, const char **argv, char **envp);
|
||||||
extern int cmd_count_objects(int argc, const char **argv, char **envp);
|
extern int cmd_count_objects(int argc, const char **argv, char **envp);
|
||||||
|
|
||||||
extern int cmd_push(int argc, const char **argv, char **envp);
|
extern int cmd_push(int argc, const char **argv, char **envp);
|
||||||
@ -29,5 +30,18 @@ extern int cmd_add(int argc, const char **argv, char **envp);
|
|||||||
extern int cmd_rev_list(int argc, const char **argv, char **envp);
|
extern int cmd_rev_list(int argc, const char **argv, char **envp);
|
||||||
extern int cmd_check_ref_format(int argc, const char **argv, char **envp);
|
extern int cmd_check_ref_format(int argc, const char **argv, char **envp);
|
||||||
extern int cmd_init_db(int argc, const char **argv, char **envp);
|
extern int cmd_init_db(int argc, const char **argv, char **envp);
|
||||||
|
extern int cmd_tar_tree(int argc, const char **argv, char **envp);
|
||||||
|
extern int cmd_upload_tar(int argc, const char **argv, char **envp);
|
||||||
|
extern int cmd_ls_files(int argc, const char **argv, char **envp);
|
||||||
|
extern int cmd_ls_tree(int argc, const char **argv, char **envp);
|
||||||
|
extern int cmd_read_tree(int argc, const char **argv, char **envp);
|
||||||
|
extern int cmd_commit_tree(int argc, const char **argv, char **envp);
|
||||||
|
extern int cmd_apply(int argc, const char **argv, char **envp);
|
||||||
|
extern int cmd_show_branch(int argc, const char **argv, char **envp);
|
||||||
|
extern int cmd_diff_files(int argc, const char **argv, char **envp);
|
||||||
|
extern int cmd_diff_index(int argc, const char **argv, char **envp);
|
||||||
|
extern int cmd_diff_stages(int argc, const char **argv, char **envp);
|
||||||
|
extern int cmd_diff_tree(int argc, const char **argv, char **envp);
|
||||||
|
extern int cmd_cat_file(int argc, const char **argv, char **envp);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
2
cache.h
2
cache.h
@ -156,6 +156,7 @@ extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int);
|
|||||||
extern int ce_modified(struct cache_entry *ce, struct stat *st, int);
|
extern int ce_modified(struct cache_entry *ce, struct stat *st, int);
|
||||||
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
|
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
|
||||||
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type);
|
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type);
|
||||||
|
extern int read_pipe(int fd, char** return_buf, unsigned long* return_size);
|
||||||
extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
|
extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
|
||||||
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
|
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
|
||||||
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
|
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
|
||||||
@ -259,6 +260,7 @@ extern void *read_object_with_reference(const unsigned char *sha1,
|
|||||||
unsigned char *sha1_ret);
|
unsigned char *sha1_ret);
|
||||||
|
|
||||||
const char *show_date(unsigned long time, int timezone);
|
const char *show_date(unsigned long time, int timezone);
|
||||||
|
const char *show_rfc2822_date(unsigned long time, int timezone);
|
||||||
int parse_date(const char *date, char *buf, int bufsize);
|
int parse_date(const char *date, char *buf, int bufsize);
|
||||||
void datestamp(char *buf, int bufsize);
|
void datestamp(char *buf, int bufsize);
|
||||||
unsigned long approxidate(const char *);
|
unsigned long approxidate(const char *);
|
||||||
|
122
commit.c
122
commit.c
@ -30,6 +30,7 @@ struct cmt_fmt_map {
|
|||||||
{ "raw", 1, CMIT_FMT_RAW },
|
{ "raw", 1, CMIT_FMT_RAW },
|
||||||
{ "medium", 1, CMIT_FMT_MEDIUM },
|
{ "medium", 1, CMIT_FMT_MEDIUM },
|
||||||
{ "short", 1, CMIT_FMT_SHORT },
|
{ "short", 1, CMIT_FMT_SHORT },
|
||||||
|
{ "email", 1, CMIT_FMT_EMAIL },
|
||||||
{ "full", 5, CMIT_FMT_FULL },
|
{ "full", 5, CMIT_FMT_FULL },
|
||||||
{ "fuller", 5, CMIT_FMT_FULLER },
|
{ "fuller", 5, CMIT_FMT_FULLER },
|
||||||
{ "oneline", 1, CMIT_FMT_ONELINE },
|
{ "oneline", 1, CMIT_FMT_ONELINE },
|
||||||
@ -421,6 +422,46 @@ static int get_one_line(const char *msg, unsigned long len)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int is_rfc2047_special(char ch)
|
||||||
|
{
|
||||||
|
return ((ch & 0x80) || (ch == '=') || (ch == '?') || (ch == '_'));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int add_rfc2047(char *buf, const char *line, int len)
|
||||||
|
{
|
||||||
|
char *bp = buf;
|
||||||
|
int i, needquote;
|
||||||
|
static const char q_utf8[] = "=?utf-8?q?";
|
||||||
|
|
||||||
|
for (i = needquote = 0; !needquote && i < len; i++) {
|
||||||
|
unsigned ch = line[i];
|
||||||
|
if (ch & 0x80)
|
||||||
|
needquote++;
|
||||||
|
if ((i + 1 < len) &&
|
||||||
|
(ch == '=' && line[i+1] == '?'))
|
||||||
|
needquote++;
|
||||||
|
}
|
||||||
|
if (!needquote)
|
||||||
|
return sprintf(buf, "%.*s", len, line);
|
||||||
|
|
||||||
|
memcpy(bp, q_utf8, sizeof(q_utf8)-1);
|
||||||
|
bp += sizeof(q_utf8)-1;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
unsigned ch = line[i];
|
||||||
|
if (is_rfc2047_special(ch)) {
|
||||||
|
sprintf(bp, "=%02X", ch);
|
||||||
|
bp += 3;
|
||||||
|
}
|
||||||
|
else if (ch == ' ')
|
||||||
|
*bp++ = '_';
|
||||||
|
else
|
||||||
|
*bp++ = ch;
|
||||||
|
}
|
||||||
|
memcpy(bp, "?=", 2);
|
||||||
|
bp += 2;
|
||||||
|
return bp - buf;
|
||||||
|
}
|
||||||
|
|
||||||
static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const char *line)
|
static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const char *line)
|
||||||
{
|
{
|
||||||
char *date;
|
char *date;
|
||||||
@ -438,13 +479,35 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
|
|||||||
time = strtoul(date, &date, 10);
|
time = strtoul(date, &date, 10);
|
||||||
tz = strtol(date, NULL, 10);
|
tz = strtol(date, NULL, 10);
|
||||||
|
|
||||||
|
if (fmt == CMIT_FMT_EMAIL) {
|
||||||
|
char *name_tail = strchr(line, '<');
|
||||||
|
int display_name_length;
|
||||||
|
if (!name_tail)
|
||||||
|
return 0;
|
||||||
|
while (line < name_tail && isspace(name_tail[-1]))
|
||||||
|
name_tail--;
|
||||||
|
display_name_length = name_tail - line;
|
||||||
|
filler = "";
|
||||||
|
strcpy(buf, "From: ");
|
||||||
|
ret = strlen(buf);
|
||||||
|
ret += add_rfc2047(buf + ret, line, display_name_length);
|
||||||
|
memcpy(buf + ret, name_tail, namelen - display_name_length);
|
||||||
|
ret += namelen - display_name_length;
|
||||||
|
buf[ret++] = '\n';
|
||||||
|
}
|
||||||
|
else {
|
||||||
ret = sprintf(buf, "%s: %.*s%.*s\n", what,
|
ret = sprintf(buf, "%s: %.*s%.*s\n", what,
|
||||||
(fmt == CMIT_FMT_FULLER) ? 4 : 0,
|
(fmt == CMIT_FMT_FULLER) ? 4 : 0,
|
||||||
filler, namelen, line);
|
filler, namelen, line);
|
||||||
|
}
|
||||||
switch (fmt) {
|
switch (fmt) {
|
||||||
case CMIT_FMT_MEDIUM:
|
case CMIT_FMT_MEDIUM:
|
||||||
ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz));
|
ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz));
|
||||||
break;
|
break;
|
||||||
|
case CMIT_FMT_EMAIL:
|
||||||
|
ret += sprintf(buf + ret, "Date: %s\n",
|
||||||
|
show_rfc2822_date(time, tz));
|
||||||
|
break;
|
||||||
case CMIT_FMT_FULLER:
|
case CMIT_FMT_FULLER:
|
||||||
ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz));
|
ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz));
|
||||||
break;
|
break;
|
||||||
@ -455,10 +518,12 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_empty_line(const char *line, int len)
|
static int is_empty_line(const char *line, int *len_p)
|
||||||
{
|
{
|
||||||
|
int len = *len_p;
|
||||||
while (len && isspace(line[len-1]))
|
while (len && isspace(line[len-1]))
|
||||||
len--;
|
len--;
|
||||||
|
*len_p = len;
|
||||||
return !len;
|
return !len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,7 +532,8 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com
|
|||||||
struct commit_list *parent = commit->parents;
|
struct commit_list *parent = commit->parents;
|
||||||
int offset;
|
int offset;
|
||||||
|
|
||||||
if ((fmt == CMIT_FMT_ONELINE) || !parent || !parent->next)
|
if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
|
||||||
|
!parent || !parent->next)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
offset = sprintf(buf, "Merge:");
|
offset = sprintf(buf, "Merge:");
|
||||||
@ -486,13 +552,30 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com
|
|||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev)
|
unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject)
|
||||||
{
|
{
|
||||||
int hdr = 1, body = 0;
|
int hdr = 1, body = 0;
|
||||||
unsigned long offset = 0;
|
unsigned long offset = 0;
|
||||||
int indent = (fmt == CMIT_FMT_ONELINE) ? 0 : 4;
|
int indent = 4;
|
||||||
int parents_shown = 0;
|
int parents_shown = 0;
|
||||||
const char *msg = commit->buffer;
|
const char *msg = commit->buffer;
|
||||||
|
int plain_non_ascii = 0;
|
||||||
|
|
||||||
|
if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
|
||||||
|
indent = 0;
|
||||||
|
|
||||||
|
/* After-subject is used to pass in Content-Type: multipart
|
||||||
|
* MIME header; in that case we do not have to do the
|
||||||
|
* plaintext content type even if the commit message has
|
||||||
|
* non 7-bit ASCII character. Otherwise, check if we need
|
||||||
|
* to say this is not a 7-bit ASCII.
|
||||||
|
*/
|
||||||
|
if (fmt == CMIT_FMT_EMAIL && !after_subject) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; !plain_non_ascii && msg[i] && i < len; i++)
|
||||||
|
if (msg[i] & 0x80)
|
||||||
|
plain_non_ascii = 1;
|
||||||
|
}
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const char *line = msg;
|
const char *line = msg;
|
||||||
@ -516,7 +599,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
|
|||||||
if (hdr) {
|
if (hdr) {
|
||||||
if (linelen == 1) {
|
if (linelen == 1) {
|
||||||
hdr = 0;
|
hdr = 0;
|
||||||
if (fmt != CMIT_FMT_ONELINE)
|
if ((fmt != CMIT_FMT_ONELINE) && !subject)
|
||||||
buf[offset++] = '\n';
|
buf[offset++] = '\n';
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -554,20 +637,47 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_empty_line(line, linelen)) {
|
if (is_empty_line(line, &linelen)) {
|
||||||
if (!body)
|
if (!body)
|
||||||
continue;
|
continue;
|
||||||
|
if (subject)
|
||||||
|
continue;
|
||||||
if (fmt == CMIT_FMT_SHORT)
|
if (fmt == CMIT_FMT_SHORT)
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
body = 1;
|
body = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (subject) {
|
||||||
|
int slen = strlen(subject);
|
||||||
|
memcpy(buf + offset, subject, slen);
|
||||||
|
offset += slen;
|
||||||
|
offset += add_rfc2047(buf + offset, line, linelen);
|
||||||
|
}
|
||||||
|
else {
|
||||||
memset(buf + offset, ' ', indent);
|
memset(buf + offset, ' ', indent);
|
||||||
memcpy(buf + offset + indent, line, linelen);
|
memcpy(buf + offset + indent, line, linelen);
|
||||||
offset += linelen + indent;
|
offset += linelen + indent;
|
||||||
|
}
|
||||||
|
buf[offset++] = '\n';
|
||||||
if (fmt == CMIT_FMT_ONELINE)
|
if (fmt == CMIT_FMT_ONELINE)
|
||||||
break;
|
break;
|
||||||
|
if (subject && plain_non_ascii) {
|
||||||
|
static const char header[] =
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n";
|
||||||
|
memcpy(buf + offset, header, sizeof(header)-1);
|
||||||
|
offset += sizeof(header)-1;
|
||||||
|
}
|
||||||
|
if (after_subject) {
|
||||||
|
int slen = strlen(after_subject);
|
||||||
|
if (slen > space - offset - 1)
|
||||||
|
slen = space - offset - 1;
|
||||||
|
memcpy(buf + offset, after_subject, slen);
|
||||||
|
offset += slen;
|
||||||
|
after_subject = NULL;
|
||||||
|
}
|
||||||
|
subject = NULL;
|
||||||
}
|
}
|
||||||
while (offset && isspace(buf[offset-1]))
|
while (offset && isspace(buf[offset-1]))
|
||||||
offset--;
|
offset--;
|
||||||
|
3
commit.h
3
commit.h
@ -45,12 +45,13 @@ enum cmit_fmt {
|
|||||||
CMIT_FMT_FULL,
|
CMIT_FMT_FULL,
|
||||||
CMIT_FMT_FULLER,
|
CMIT_FMT_FULLER,
|
||||||
CMIT_FMT_ONELINE,
|
CMIT_FMT_ONELINE,
|
||||||
|
CMIT_FMT_EMAIL,
|
||||||
|
|
||||||
CMIT_FMT_UNSPECIFIED,
|
CMIT_FMT_UNSPECIFIED,
|
||||||
};
|
};
|
||||||
|
|
||||||
extern enum cmit_fmt get_commit_format(const char *arg);
|
extern enum cmit_fmt get_commit_format(const char *arg);
|
||||||
extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev);
|
extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject);
|
||||||
|
|
||||||
/** Removes the first commit from a list sorted by date, and adds all
|
/** Removes the first commit from a list sorted by date, and adds all
|
||||||
* of its parents.
|
* of its parents.
|
||||||
|
200
compat/inet_ntop.c
Normal file
200
compat/inet_ntop.c
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1996-1999 by Internet Software Consortium.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
|
||||||
|
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
|
||||||
|
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||||
|
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef NS_INADDRSZ
|
||||||
|
#define NS_INADDRSZ 4
|
||||||
|
#endif
|
||||||
|
#ifndef NS_IN6ADDRSZ
|
||||||
|
#define NS_IN6ADDRSZ 16
|
||||||
|
#endif
|
||||||
|
#ifndef NS_INT16SZ
|
||||||
|
#define NS_INT16SZ 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WARNING: Don't even consider trying to compile this on a system where
|
||||||
|
* sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* const char *
|
||||||
|
* inet_ntop4(src, dst, size)
|
||||||
|
* format an IPv4 address
|
||||||
|
* return:
|
||||||
|
* `dst' (as a const)
|
||||||
|
* notes:
|
||||||
|
* (1) uses no statics
|
||||||
|
* (2) takes a u_char* not an in_addr as input
|
||||||
|
* author:
|
||||||
|
* Paul Vixie, 1996.
|
||||||
|
*/
|
||||||
|
static const char *
|
||||||
|
inet_ntop4(src, dst, size)
|
||||||
|
const u_char *src;
|
||||||
|
char *dst;
|
||||||
|
size_t size;
|
||||||
|
{
|
||||||
|
static const char fmt[] = "%u.%u.%u.%u";
|
||||||
|
char tmp[sizeof "255.255.255.255"];
|
||||||
|
int nprinted;
|
||||||
|
|
||||||
|
nprinted = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);
|
||||||
|
if (nprinted < 0)
|
||||||
|
return (NULL); /* we assume "errno" was set by "snprintf()" */
|
||||||
|
if ((size_t)nprinted > size) {
|
||||||
|
errno = ENOSPC;
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
strcpy(dst, tmp);
|
||||||
|
return (dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NO_IPV6
|
||||||
|
/* const char *
|
||||||
|
* inet_ntop6(src, dst, size)
|
||||||
|
* convert IPv6 binary address into presentation (printable) format
|
||||||
|
* author:
|
||||||
|
* Paul Vixie, 1996.
|
||||||
|
*/
|
||||||
|
static const char *
|
||||||
|
inet_ntop6(src, dst, size)
|
||||||
|
const u_char *src;
|
||||||
|
char *dst;
|
||||||
|
size_t size;
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Note that int32_t and int16_t need only be "at least" large enough
|
||||||
|
* to contain a value of the specified size. On some systems, like
|
||||||
|
* Crays, there is no such thing as an integer variable with 16 bits.
|
||||||
|
* Keep this in mind if you think this function should have been coded
|
||||||
|
* to use pointer overlays. All the world's not a VAX.
|
||||||
|
*/
|
||||||
|
char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
|
||||||
|
struct { int base, len; } best, cur;
|
||||||
|
u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Preprocess:
|
||||||
|
* Copy the input (bytewise) array into a wordwise array.
|
||||||
|
* Find the longest run of 0x00's in src[] for :: shorthanding.
|
||||||
|
*/
|
||||||
|
memset(words, '\0', sizeof words);
|
||||||
|
for (i = 0; i < NS_IN6ADDRSZ; i++)
|
||||||
|
words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
|
||||||
|
best.base = -1;
|
||||||
|
cur.base = -1;
|
||||||
|
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
|
||||||
|
if (words[i] == 0) {
|
||||||
|
if (cur.base == -1)
|
||||||
|
cur.base = i, cur.len = 1;
|
||||||
|
else
|
||||||
|
cur.len++;
|
||||||
|
} else {
|
||||||
|
if (cur.base != -1) {
|
||||||
|
if (best.base == -1 || cur.len > best.len)
|
||||||
|
best = cur;
|
||||||
|
cur.base = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cur.base != -1) {
|
||||||
|
if (best.base == -1 || cur.len > best.len)
|
||||||
|
best = cur;
|
||||||
|
}
|
||||||
|
if (best.base != -1 && best.len < 2)
|
||||||
|
best.base = -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Format the result.
|
||||||
|
*/
|
||||||
|
tp = tmp;
|
||||||
|
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
|
||||||
|
/* Are we inside the best run of 0x00's? */
|
||||||
|
if (best.base != -1 && i >= best.base &&
|
||||||
|
i < (best.base + best.len)) {
|
||||||
|
if (i == best.base)
|
||||||
|
*tp++ = ':';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Are we following an initial run of 0x00s or any real hex? */
|
||||||
|
if (i != 0)
|
||||||
|
*tp++ = ':';
|
||||||
|
/* Is this address an encapsulated IPv4? */
|
||||||
|
if (i == 6 && best.base == 0 &&
|
||||||
|
(best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
|
||||||
|
if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp)))
|
||||||
|
return (NULL);
|
||||||
|
tp += strlen(tp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tp += snprintf(tp, sizeof tmp - (tp - tmp), "%x", words[i]);
|
||||||
|
}
|
||||||
|
/* Was it a trailing run of 0x00's? */
|
||||||
|
if (best.base != -1 && (best.base + best.len) ==
|
||||||
|
(NS_IN6ADDRSZ / NS_INT16SZ))
|
||||||
|
*tp++ = ':';
|
||||||
|
*tp++ = '\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check for overflow, copy, and we're done.
|
||||||
|
*/
|
||||||
|
if ((size_t)(tp - tmp) > size) {
|
||||||
|
errno = ENOSPC;
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
strcpy(dst, tmp);
|
||||||
|
return (dst);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* char *
|
||||||
|
* inet_ntop(af, src, dst, size)
|
||||||
|
* convert a network format address to presentation format.
|
||||||
|
* return:
|
||||||
|
* pointer to presentation format address (`dst'), or NULL (see errno).
|
||||||
|
* author:
|
||||||
|
* Paul Vixie, 1996.
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
inet_ntop(af, src, dst, size)
|
||||||
|
int af;
|
||||||
|
const void *src;
|
||||||
|
char *dst;
|
||||||
|
size_t size;
|
||||||
|
{
|
||||||
|
switch (af) {
|
||||||
|
case AF_INET:
|
||||||
|
return (inet_ntop4(src, dst, size));
|
||||||
|
#ifndef NO_IPV6
|
||||||
|
case AF_INET6:
|
||||||
|
return (inet_ntop6(src, dst, size));
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
errno = EAFNOSUPPORT;
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
/* NOTREACHED */
|
||||||
|
}
|
@ -100,7 +100,7 @@ int path_match(const char *path, int nr, char **match)
|
|||||||
if (pathlen > len && path[pathlen - len - 1] != '/')
|
if (pathlen > len && path[pathlen - len - 1] != '/')
|
||||||
continue;
|
continue;
|
||||||
*s = 0;
|
*s = 0;
|
||||||
return 1;
|
return (i + 1);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ git-svn.html : git-svn.txt
|
|||||||
-f ../../Documentation/asciidoc.conf $<
|
-f ../../Documentation/asciidoc.conf $<
|
||||||
test: git-svn
|
test: git-svn
|
||||||
cd t && $(SHELL) ./t0000-contrib-git-svn.sh
|
cd t && $(SHELL) ./t0000-contrib-git-svn.sh
|
||||||
|
cd t && $(SHELL) ./t0001-contrib-git-svn-props.sh
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f git-svn *.xml *.html *.1
|
rm -f git-svn *.xml *.html *.1
|
||||||
|
@ -8,7 +8,7 @@ use vars qw/ $AUTHOR $VERSION
|
|||||||
$GIT_SVN_INDEX $GIT_SVN
|
$GIT_SVN_INDEX $GIT_SVN
|
||||||
$GIT_DIR $REV_DIR/;
|
$GIT_DIR $REV_DIR/;
|
||||||
$AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
|
$AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
|
||||||
$VERSION = '1.0.0';
|
$VERSION = '1.1.0-pre';
|
||||||
|
|
||||||
use Cwd qw/abs_path/;
|
use Cwd qw/abs_path/;
|
||||||
$GIT_DIR = abs_path($ENV{GIT_DIR} || '.git');
|
$GIT_DIR = abs_path($ENV{GIT_DIR} || '.git');
|
||||||
@ -39,6 +39,10 @@ my $_svn_co_url_revs;
|
|||||||
my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
|
my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
|
||||||
'branch|b=s' => \@_branch_from,
|
'branch|b=s' => \@_branch_from,
|
||||||
'authors-file|A=s' => \$_authors );
|
'authors-file|A=s' => \$_authors );
|
||||||
|
|
||||||
|
# yes, 'native' sets "\n". Patches to fix this for non-*nix systems welcome:
|
||||||
|
my %EOL = ( CR => "\015", LF => "\012", CRLF => "\015\012", native => "\012" );
|
||||||
|
|
||||||
my %cmd = (
|
my %cmd = (
|
||||||
fetch => [ \&fetch, "Download new revisions from SVN",
|
fetch => [ \&fetch, "Download new revisions from SVN",
|
||||||
{ 'revision|r=s' => \$_revision, %fc_opts } ],
|
{ 'revision|r=s' => \$_revision, %fc_opts } ],
|
||||||
@ -207,7 +211,7 @@ sub rebuild {
|
|||||||
push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
|
push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
|
||||||
sys(@svn_up,"-r$newest_rev");
|
sys(@svn_up,"-r$newest_rev");
|
||||||
$ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX;
|
$ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX;
|
||||||
git_addremove();
|
index_changes();
|
||||||
exec('git-write-tree');
|
exec('git-write-tree');
|
||||||
}
|
}
|
||||||
waitpid $pid, 0;
|
waitpid $pid, 0;
|
||||||
@ -249,7 +253,7 @@ sub fetch {
|
|||||||
chdir $SVN_WC or croak $!;
|
chdir $SVN_WC or croak $!;
|
||||||
read_uuid();
|
read_uuid();
|
||||||
$last_commit = git_commit($base, @parents);
|
$last_commit = git_commit($base, @parents);
|
||||||
assert_svn_wc_clean($base->{revision}, $last_commit);
|
assert_tree($last_commit);
|
||||||
} else {
|
} else {
|
||||||
chdir $SVN_WC or croak $!;
|
chdir $SVN_WC or croak $!;
|
||||||
read_uuid();
|
read_uuid();
|
||||||
@ -259,16 +263,20 @@ sub fetch {
|
|||||||
push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
|
push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
|
||||||
my $last = $base;
|
my $last = $base;
|
||||||
while (my $log_msg = next_log_entry($svn_log)) {
|
while (my $log_msg = next_log_entry($svn_log)) {
|
||||||
assert_svn_wc_clean($last->{revision}, $last_commit);
|
assert_tree($last_commit);
|
||||||
if ($last->{revision} >= $log_msg->{revision}) {
|
if ($last->{revision} >= $log_msg->{revision}) {
|
||||||
croak "Out of order: last >= current: ",
|
croak "Out of order: last >= current: ",
|
||||||
"$last->{revision} >= $log_msg->{revision}\n";
|
"$last->{revision} >= $log_msg->{revision}\n";
|
||||||
}
|
}
|
||||||
|
# Revert is needed for cases like:
|
||||||
|
# https://svn.musicpd.org/Jamming/trunk (r166:167), but
|
||||||
|
# I can't seem to reproduce something like that on a test...
|
||||||
|
sys(qw/svn revert -R ./);
|
||||||
|
assert_svn_wc_clean($last->{revision});
|
||||||
sys(@svn_up,"-r$log_msg->{revision}");
|
sys(@svn_up,"-r$log_msg->{revision}");
|
||||||
$last_commit = git_commit($log_msg, $last_commit, @parents);
|
$last_commit = git_commit($log_msg, $last_commit, @parents);
|
||||||
$last = $log_msg;
|
$last = $log_msg;
|
||||||
}
|
}
|
||||||
assert_svn_wc_clean($last->{revision}, $last_commit);
|
|
||||||
unless (-e "$GIT_DIR/refs/heads/master") {
|
unless (-e "$GIT_DIR/refs/heads/master") {
|
||||||
sys(qw(git-update-ref refs/heads/master),$last_commit);
|
sys(qw(git-update-ref refs/heads/master),$last_commit);
|
||||||
}
|
}
|
||||||
@ -314,7 +322,6 @@ sub commit {
|
|||||||
$svn_current_rev = svn_commit_tree($svn_current_rev, $c);
|
$svn_current_rev = svn_commit_tree($svn_current_rev, $c);
|
||||||
}
|
}
|
||||||
print "Done committing ",scalar @revs," revisions to SVN\n";
|
print "Done committing ",scalar @revs," revisions to SVN\n";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub show_ignore {
|
sub show_ignore {
|
||||||
@ -367,13 +374,11 @@ sub setup_git_svn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub assert_svn_wc_clean {
|
sub assert_svn_wc_clean {
|
||||||
my ($svn_rev, $treeish) = @_;
|
my ($svn_rev) = @_;
|
||||||
croak "$svn_rev is not an integer!\n" unless ($svn_rev =~ /^\d+$/);
|
croak "$svn_rev is not an integer!\n" unless ($svn_rev =~ /^\d+$/);
|
||||||
croak "$treeish is not a sha1!\n" unless ($treeish =~ /^$sha1$/o);
|
|
||||||
my $lcr = svn_info('.')->{'Last Changed Rev'};
|
my $lcr = svn_info('.')->{'Last Changed Rev'};
|
||||||
if ($svn_rev != $lcr) {
|
if ($svn_rev != $lcr) {
|
||||||
print STDERR "Checking for copy-tree ... ";
|
print STDERR "Checking for copy-tree ... ";
|
||||||
# use
|
|
||||||
my @diff = grep(/^Index: /,(safe_qx(qw(svn diff),
|
my @diff = grep(/^Index: /,(safe_qx(qw(svn diff),
|
||||||
"-r$lcr:$svn_rev")));
|
"-r$lcr:$svn_rev")));
|
||||||
if (@diff) {
|
if (@diff) {
|
||||||
@ -389,7 +394,6 @@ sub assert_svn_wc_clean {
|
|||||||
print STDERR $_ foreach @status;
|
print STDERR $_ foreach @status;
|
||||||
croak;
|
croak;
|
||||||
}
|
}
|
||||||
assert_tree($treeish);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub assert_tree {
|
sub assert_tree {
|
||||||
@ -416,7 +420,7 @@ sub assert_tree {
|
|||||||
unlink $tmpindex or croak $!;
|
unlink $tmpindex or croak $!;
|
||||||
}
|
}
|
||||||
$ENV{GIT_INDEX_FILE} = $tmpindex;
|
$ENV{GIT_INDEX_FILE} = $tmpindex;
|
||||||
git_addremove();
|
index_changes(1);
|
||||||
chomp(my $tree = `git-write-tree`);
|
chomp(my $tree = `git-write-tree`);
|
||||||
if ($old_index) {
|
if ($old_index) {
|
||||||
$ENV{GIT_INDEX_FILE} = $old_index;
|
$ENV{GIT_INDEX_FILE} = $old_index;
|
||||||
@ -426,6 +430,7 @@ sub assert_tree {
|
|||||||
if ($tree ne $expected) {
|
if ($tree ne $expected) {
|
||||||
croak "Tree mismatch, Got: $tree, Expected: $expected\n";
|
croak "Tree mismatch, Got: $tree, Expected: $expected\n";
|
||||||
}
|
}
|
||||||
|
unlink $tmpindex;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub parse_diff_tree {
|
sub parse_diff_tree {
|
||||||
@ -562,7 +567,8 @@ sub precommit_check {
|
|||||||
sub svn_checkout_tree {
|
sub svn_checkout_tree {
|
||||||
my ($svn_rev, $treeish) = @_;
|
my ($svn_rev, $treeish) = @_;
|
||||||
my $from = file_to_s("$REV_DIR/$svn_rev");
|
my $from = file_to_s("$REV_DIR/$svn_rev");
|
||||||
assert_svn_wc_clean($svn_rev,$from);
|
assert_svn_wc_clean($svn_rev);
|
||||||
|
assert_tree($from);
|
||||||
print "diff-tree $from $treeish\n";
|
print "diff-tree $from $treeish\n";
|
||||||
my $pid = open my $diff_fh, '-|';
|
my $pid = open my $diff_fh, '-|';
|
||||||
defined $pid or croak $!;
|
defined $pid or croak $!;
|
||||||
@ -852,13 +858,75 @@ sub svn_info {
|
|||||||
|
|
||||||
sub sys { system(@_) == 0 or croak $? }
|
sub sys { system(@_) == 0 or croak $? }
|
||||||
|
|
||||||
sub git_addremove {
|
sub eol_cp {
|
||||||
system( "git-diff-files --name-only -z ".
|
my ($from, $to) = @_;
|
||||||
" | git-update-index --remove -z --stdin && ".
|
my $es = safe_qx(qw/svn propget svn:eol-style/, $to);
|
||||||
"git-ls-files -z --others ".
|
open my $rfd, '<', $from or croak $!;
|
||||||
"'--exclude-from=$GIT_DIR/$GIT_SVN/info/exclude'".
|
binmode $rfd or croak $!;
|
||||||
" | git-update-index --add -z --stdin"
|
open my $wfd, '>', $to or croak $!;
|
||||||
) == 0 or croak $?
|
binmode $wfd or croak $!;
|
||||||
|
|
||||||
|
my $eol = $EOL{$es} or undef;
|
||||||
|
if ($eol) {
|
||||||
|
print "$eol: $from => $to\n";
|
||||||
|
}
|
||||||
|
my $buf;
|
||||||
|
while (1) {
|
||||||
|
my ($r, $w, $t);
|
||||||
|
defined($r = sysread($rfd, $buf, 4096)) or croak $!;
|
||||||
|
return unless $r;
|
||||||
|
$buf =~ s/(?:\015|\012|\015\012)/$eol/gs if $eol;
|
||||||
|
for ($w = 0; $w < $r; $w += $t) {
|
||||||
|
$t = syswrite($wfd, $buf, $r - $w, $w) or croak $!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_update_index {
|
||||||
|
my ($z_cmd, $cmd, $no_text_base) = @_;
|
||||||
|
|
||||||
|
my $z = open my $p, '-|';
|
||||||
|
defined $z or croak $!;
|
||||||
|
unless ($z) { exec @$z_cmd or croak $! }
|
||||||
|
|
||||||
|
my $pid = open my $ui, '|-';
|
||||||
|
defined $pid or croak $!;
|
||||||
|
unless ($pid) {
|
||||||
|
exec('git-update-index',"--$cmd",'-z','--stdin') or croak $!;
|
||||||
|
}
|
||||||
|
local $/ = "\0";
|
||||||
|
while (my $x = <$p>) {
|
||||||
|
chomp $x;
|
||||||
|
if (!$no_text_base && lstat $x && ! -l _ &&
|
||||||
|
safe_qx(qw/svn propget svn:keywords/,$x)) {
|
||||||
|
my $mode = -x _ ? 0755 : 0644;
|
||||||
|
my ($v,$d,$f) = File::Spec->splitpath($x);
|
||||||
|
my $tb = File::Spec->catfile($d, '.svn', 'tmp',
|
||||||
|
'text-base',"$f.svn-base");
|
||||||
|
$tb =~ s#^/##;
|
||||||
|
unless (-f $tb) {
|
||||||
|
$tb = File::Spec->catfile($d, '.svn',
|
||||||
|
'text-base',"$f.svn-base");
|
||||||
|
$tb =~ s#^/##;
|
||||||
|
}
|
||||||
|
unlink $x or croak $!;
|
||||||
|
eol_cp($tb, $x);
|
||||||
|
chmod(($mode &~ umask), $x) or croak $!;
|
||||||
|
}
|
||||||
|
print $ui $x,"\0";
|
||||||
|
}
|
||||||
|
close $ui or croak $!;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub index_changes {
|
||||||
|
my $no_text_base = shift;
|
||||||
|
do_update_index([qw/git-diff-files --name-only -z/],
|
||||||
|
'remove',
|
||||||
|
$no_text_base);
|
||||||
|
do_update_index([qw/git-ls-files -z --others/,
|
||||||
|
"--exclude-from=$GIT_DIR/$GIT_SVN/info/exclude"],
|
||||||
|
'add',
|
||||||
|
$no_text_base);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub s_to_file {
|
sub s_to_file {
|
||||||
@ -936,7 +1004,7 @@ sub git_commit {
|
|||||||
defined $pid or croak $!;
|
defined $pid or croak $!;
|
||||||
if ($pid == 0) {
|
if ($pid == 0) {
|
||||||
$ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX;
|
$ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX;
|
||||||
git_addremove();
|
index_changes();
|
||||||
chomp(my $tree = `git-write-tree`);
|
chomp(my $tree = `git-write-tree`);
|
||||||
croak if $?;
|
croak if $?;
|
||||||
if (exists $tree_map{$tree}) {
|
if (exists $tree_map{$tree}) {
|
||||||
|
39
contrib/git-svn/t/lib-git-svn.sh
Normal file
39
contrib/git-svn/t/lib-git-svn.sh
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
PATH=$PWD/../:$PATH
|
||||||
|
if test -d ../../../t
|
||||||
|
then
|
||||||
|
cd ../../../t
|
||||||
|
else
|
||||||
|
echo "Must be run in contrib/git-svn/t" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
GIT_DIR=$PWD/.git
|
||||||
|
GIT_SVN_DIR=$GIT_DIR/git-svn
|
||||||
|
SVN_TREE=$GIT_SVN_DIR/tree
|
||||||
|
|
||||||
|
svnadmin >/dev/null 2>&1
|
||||||
|
if test $? != 1
|
||||||
|
then
|
||||||
|
test_expect_success 'skipping contrib/git-svn test' :
|
||||||
|
test_done
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
svn >/dev/null 2>&1
|
||||||
|
if test $? != 1
|
||||||
|
then
|
||||||
|
test_expect_success 'skipping contrib/git-svn test' :
|
||||||
|
test_done
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
svnrepo=$PWD/svnrepo
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
svnadmin create $svnrepo
|
||||||
|
svnrepo="file://$svnrepo/test-git-svn"
|
||||||
|
|
||||||
|
|
@ -3,48 +3,10 @@
|
|||||||
# Copyright (c) 2006 Eric Wong
|
# Copyright (c) 2006 Eric Wong
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
PATH=$PWD/../:$PATH
|
|
||||||
test_description='git-svn tests'
|
test_description='git-svn tests'
|
||||||
if test -d ../../../t
|
. ./lib-git-svn.sh
|
||||||
then
|
|
||||||
cd ../../../t
|
|
||||||
else
|
|
||||||
echo "Must be run in contrib/git-svn/t" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
. ./test-lib.sh
|
|
||||||
|
|
||||||
GIT_DIR=$PWD/.git
|
|
||||||
GIT_SVN_DIR=$GIT_DIR/git-svn
|
|
||||||
SVN_TREE=$GIT_SVN_DIR/tree
|
|
||||||
|
|
||||||
svnadmin >/dev/null 2>&1
|
|
||||||
if test $? != 1
|
|
||||||
then
|
|
||||||
test_expect_success 'skipping contrib/git-svn test' :
|
|
||||||
test_done
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
svn >/dev/null 2>&1
|
|
||||||
if test $? != 1
|
|
||||||
then
|
|
||||||
test_expect_success 'skipping contrib/git-svn test' :
|
|
||||||
test_done
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
svnrepo=$PWD/svnrepo
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
svnadmin create $svnrepo
|
|
||||||
svnrepo="file://$svnrepo/test-git-svn"
|
|
||||||
|
|
||||||
mkdir import
|
mkdir import
|
||||||
|
|
||||||
cd import
|
cd import
|
||||||
|
|
||||||
echo foo > foo
|
echo foo > foo
|
||||||
@ -55,10 +17,9 @@ mkdir -p bar
|
|||||||
echo 'zzz' > bar/zzz
|
echo 'zzz' > bar/zzz
|
||||||
echo '#!/bin/sh' > exec.sh
|
echo '#!/bin/sh' > exec.sh
|
||||||
chmod +x exec.sh
|
chmod +x exec.sh
|
||||||
svn import -m 'import for git-svn' . $svnrepo >/dev/null
|
svn import -m 'import for git-svn' . "$svnrepo" >/dev/null
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
rm -rf import
|
rm -rf import
|
||||||
|
|
||||||
test_expect_success \
|
test_expect_success \
|
||||||
|
125
contrib/git-svn/t/t0001-contrib-git-svn-props.sh
Normal file
125
contrib/git-svn/t/t0001-contrib-git-svn-props.sh
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Copyright (c) 2006 Eric Wong
|
||||||
|
#
|
||||||
|
|
||||||
|
test_description='git-svn property tests'
|
||||||
|
. ./lib-git-svn.sh
|
||||||
|
|
||||||
|
mkdir import
|
||||||
|
|
||||||
|
a_crlf=
|
||||||
|
a_lf=
|
||||||
|
a_cr=
|
||||||
|
a_ne_crlf=
|
||||||
|
a_ne_lf=
|
||||||
|
a_ne_cr=
|
||||||
|
a_empty=
|
||||||
|
a_empty_lf=
|
||||||
|
a_empty_cr=
|
||||||
|
a_empty_crlf=
|
||||||
|
|
||||||
|
cd import
|
||||||
|
cat >> kw.c <<''
|
||||||
|
/* Make it look like somebody copied a file from CVS into SVN: */
|
||||||
|
/* $Id: kw.c,v 1.1.1.1 1994/03/06 00:00:00 eric Exp $ */
|
||||||
|
|
||||||
|
printf "Hello\r\nWorld\r\n" > crlf
|
||||||
|
a_crlf=`git-hash-object -w crlf`
|
||||||
|
printf "Hello\rWorld\r" > cr
|
||||||
|
a_cr=`git-hash-object -w cr`
|
||||||
|
printf "Hello\nWorld\n" > lf
|
||||||
|
a_lf=`git-hash-object -w lf`
|
||||||
|
|
||||||
|
printf "Hello\r\nWorld" > ne_crlf
|
||||||
|
a_ne_crlf=`git-hash-object -w ne_crlf`
|
||||||
|
printf "Hello\nWorld" > ne_lf
|
||||||
|
a_ne_lf=`git-hash-object -w ne_lf`
|
||||||
|
printf "Hello\rWorld" > ne_cr
|
||||||
|
a_ne_cr=`git-hash-object -w ne_cr`
|
||||||
|
|
||||||
|
touch empty
|
||||||
|
a_empty=`git-hash-object -w empty`
|
||||||
|
printf "\n" > empty_lf
|
||||||
|
a_empty_lf=`git-hash-object -w empty_lf`
|
||||||
|
printf "\r" > empty_cr
|
||||||
|
a_empty_cr=`git-hash-object -w empty_cr`
|
||||||
|
printf "\r\n" > empty_crlf
|
||||||
|
a_empty_crlf=`git-hash-object -w empty_crlf`
|
||||||
|
|
||||||
|
svn import -m 'import for git-svn' . "$svnrepo" >/dev/null
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
rm -rf import
|
||||||
|
svn co "$svnrepo" test_wc
|
||||||
|
|
||||||
|
cd test_wc
|
||||||
|
echo 'Greetings' >> kw.c
|
||||||
|
svn commit -m 'Not yet an $Id$'
|
||||||
|
svn up
|
||||||
|
|
||||||
|
echo 'Hello world' >> kw.c
|
||||||
|
svn commit -m 'Modified file, but still not yet an $Id$'
|
||||||
|
svn up
|
||||||
|
|
||||||
|
svn propset svn:keywords Id kw.c
|
||||||
|
svn commit -m 'Propset $Id$'
|
||||||
|
svn up
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
git-svn init "$svnrepo"
|
||||||
|
git-svn fetch
|
||||||
|
|
||||||
|
git checkout -b mybranch remotes/git-svn
|
||||||
|
echo 'Hi again' >> kw.c
|
||||||
|
name='test svn:keywords ignoring'
|
||||||
|
|
||||||
|
git commit -a -m "$name"
|
||||||
|
git-svn commit remotes/git-svn..mybranch
|
||||||
|
git pull . remotes/git-svn
|
||||||
|
|
||||||
|
expect='/* $Id$ */'
|
||||||
|
got="`sed -ne 2p kw.c`"
|
||||||
|
test_expect_success 'raw $Id$ found in kw.c' "test '$expect' = '$got'"
|
||||||
|
|
||||||
|
cd test_wc
|
||||||
|
svn propset svn:eol-style CR empty
|
||||||
|
svn propset svn:eol-style CR crlf
|
||||||
|
svn propset svn:eol-style CR ne_crlf
|
||||||
|
svn commit -m 'propset CR on crlf files'
|
||||||
|
svn up
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
git-svn fetch
|
||||||
|
git pull . remotes/git-svn
|
||||||
|
|
||||||
|
svn co "$svnrepo" new_wc
|
||||||
|
for i in crlf ne_crlf lf ne_lf cr ne_cr empty_cr empty_lf empty empty_crlf
|
||||||
|
do
|
||||||
|
test_expect_success "Comparing $i" "cmp $i new_wc/$i"
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
cd test_wc
|
||||||
|
printf '$Id$\rHello\rWorld\r' > cr
|
||||||
|
printf '$Id$\rHello\rWorld' > ne_cr
|
||||||
|
a_cr=`printf '$Id$\r\nHello\r\nWorld\r\n' | git-hash-object --stdin`
|
||||||
|
a_ne_cr=`printf '$Id$\r\nHello\r\nWorld' | git-hash-object --stdin`
|
||||||
|
svn propset svn:eol-style CRLF cr
|
||||||
|
svn propset svn:eol-style CRLF ne_cr
|
||||||
|
svn propset svn:keywords Id cr
|
||||||
|
svn propset svn:keywords Id ne_cr
|
||||||
|
svn commit -m 'propset CRLF on cr files'
|
||||||
|
svn up
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
git-svn fetch
|
||||||
|
git pull . remotes/git-svn
|
||||||
|
|
||||||
|
b_cr="`git-hash-object cr`"
|
||||||
|
b_ne_cr="`git-hash-object ne_cr`"
|
||||||
|
|
||||||
|
test_expect_success 'CRLF + $Id$' "test '$a_cr' = '$b_cr'"
|
||||||
|
test_expect_success 'CRLF + $Id$ (no newline)' "test '$a_ne_cr' = '$b_ne_cr'"
|
||||||
|
|
||||||
|
test_done
|
@ -425,7 +425,7 @@ class DiffWindow:
|
|||||||
class GitView:
|
class GitView:
|
||||||
""" This is the main class
|
""" This is the main class
|
||||||
"""
|
"""
|
||||||
version = "0.7"
|
version = "0.8"
|
||||||
|
|
||||||
def __init__(self, with_diff=0):
|
def __init__(self, with_diff=0):
|
||||||
self.with_diff = with_diff
|
self.with_diff = with_diff
|
||||||
@ -449,8 +449,17 @@ class GitView:
|
|||||||
|
|
||||||
self.accel_group = gtk.AccelGroup()
|
self.accel_group = gtk.AccelGroup()
|
||||||
self.window.add_accel_group(self.accel_group)
|
self.window.add_accel_group(self.accel_group)
|
||||||
|
self.accel_group.connect_group(0xffc2, 0, gtk.ACCEL_LOCKED, self.refresh);
|
||||||
|
|
||||||
self.construct()
|
self.window.add(self.construct())
|
||||||
|
|
||||||
|
def refresh(self, widget, event=None, *arguments, **keywords):
|
||||||
|
self.get_encoding()
|
||||||
|
self.get_bt_sha1()
|
||||||
|
Commit.children_sha1 = {}
|
||||||
|
self.set_branch(sys.argv[without_diff:])
|
||||||
|
self.window.show()
|
||||||
|
return True
|
||||||
|
|
||||||
def get_bt_sha1(self):
|
def get_bt_sha1(self):
|
||||||
""" Update the bt_sha1 dictionary with the
|
""" Update the bt_sha1 dictionary with the
|
||||||
@ -500,9 +509,9 @@ class GitView:
|
|||||||
menu_bar.show()
|
menu_bar.show()
|
||||||
vbox.pack_start(menu_bar, expand=False, fill=True)
|
vbox.pack_start(menu_bar, expand=False, fill=True)
|
||||||
vbox.pack_start(paned, expand=True, fill=True)
|
vbox.pack_start(paned, expand=True, fill=True)
|
||||||
self.window.add(vbox)
|
|
||||||
paned.show()
|
paned.show()
|
||||||
vbox.show()
|
vbox.show()
|
||||||
|
return vbox
|
||||||
|
|
||||||
|
|
||||||
def construct_top(self):
|
def construct_top(self):
|
||||||
@ -974,10 +983,15 @@ class GitView:
|
|||||||
try:
|
try:
|
||||||
self.treeview.set_cursor(self.index[revid])
|
self.treeview.set_cursor(self.index[revid])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
print "Revision %s not present in the list" % revid
|
dialog = gtk.MessageDialog(parent=None, flags=0,
|
||||||
|
type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE,
|
||||||
|
message_format=None)
|
||||||
|
dialog.set_markup("Revision <b>%s</b> not present in the list" % revid)
|
||||||
# revid == 0 is the parent of the first commit
|
# revid == 0 is the parent of the first commit
|
||||||
if (revid != 0 ):
|
if (revid != 0 ):
|
||||||
print "Try running gitview without any options"
|
dialog.format_secondary_text("Try running gitview without any options")
|
||||||
|
dialog.run()
|
||||||
|
dialog.destroy()
|
||||||
|
|
||||||
self.treeview.grab_focus()
|
self.treeview.grab_focus()
|
||||||
|
|
||||||
@ -987,8 +1001,8 @@ class GitView:
|
|||||||
window.set_diff(commit_sha1, parent_sha1, encoding)
|
window.set_diff(commit_sha1, parent_sha1, encoding)
|
||||||
self.treeview.grab_focus()
|
self.treeview.grab_focus()
|
||||||
|
|
||||||
|
without_diff = 0
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
without_diff = 0
|
|
||||||
|
|
||||||
if (len(sys.argv) > 1 ):
|
if (len(sys.argv) > 1 ):
|
||||||
if (sys.argv[1] == "--without-diff"):
|
if (sys.argv[1] == "--without-diff"):
|
||||||
|
@ -25,6 +25,9 @@ OPTIONS
|
|||||||
|
|
||||||
<args>
|
<args>
|
||||||
All the valid option for git-rev-list(1)
|
All the valid option for git-rev-list(1)
|
||||||
|
Key Bindings:
|
||||||
|
F5:
|
||||||
|
To reread references.
|
||||||
|
|
||||||
EXAMPLES
|
EXAMPLES
|
||||||
------
|
------
|
||||||
@ -35,4 +38,3 @@ EXAMPLES
|
|||||||
gitview --since=2.weeks.ago
|
gitview --since=2.weeks.ago
|
||||||
Show the changes during the last two weeks
|
Show the changes during the last two weeks
|
||||||
|
|
||||||
|
|
||||||
|
29
date.c
29
date.c
@ -42,18 +42,24 @@ static const char *weekday_names[] = {
|
|||||||
* thing, which means that tz -0100 is passed in as the integer -100,
|
* thing, which means that tz -0100 is passed in as the integer -100,
|
||||||
* even though it means "sixty minutes off"
|
* even though it means "sixty minutes off"
|
||||||
*/
|
*/
|
||||||
const char *show_date(unsigned long time, int tz)
|
static struct tm *time_to_tm(unsigned long time, int tz)
|
||||||
{
|
{
|
||||||
struct tm *tm;
|
|
||||||
time_t t;
|
time_t t;
|
||||||
static char timebuf[200];
|
|
||||||
int minutes;
|
int minutes;
|
||||||
|
|
||||||
minutes = tz < 0 ? -tz : tz;
|
minutes = tz < 0 ? -tz : tz;
|
||||||
minutes = (minutes / 100)*60 + (minutes % 100);
|
minutes = (minutes / 100)*60 + (minutes % 100);
|
||||||
minutes = tz < 0 ? -minutes : minutes;
|
minutes = tz < 0 ? -minutes : minutes;
|
||||||
t = time + minutes * 60;
|
t = time + minutes * 60;
|
||||||
tm = gmtime(&t);
|
return gmtime(&t);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *show_date(unsigned long time, int tz)
|
||||||
|
{
|
||||||
|
struct tm *tm;
|
||||||
|
static char timebuf[200];
|
||||||
|
|
||||||
|
tm = time_to_tm(time, tz);
|
||||||
if (!tm)
|
if (!tm)
|
||||||
return NULL;
|
return NULL;
|
||||||
sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d %+05d",
|
sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d %+05d",
|
||||||
@ -65,6 +71,21 @@ const char *show_date(unsigned long time, int tz)
|
|||||||
return timebuf;
|
return timebuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *show_rfc2822_date(unsigned long time, int tz)
|
||||||
|
{
|
||||||
|
struct tm *tm;
|
||||||
|
static char timebuf[200];
|
||||||
|
|
||||||
|
tm = time_to_tm(time, tz);
|
||||||
|
if (!tm)
|
||||||
|
return NULL;
|
||||||
|
sprintf(timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
|
||||||
|
weekday_names[tm->tm_wday], tm->tm_mday,
|
||||||
|
month_names[tm->tm_mon], tm->tm_year + 1900,
|
||||||
|
tm->tm_hour, tm->tm_min, tm->tm_sec, tz);
|
||||||
|
return timebuf;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check these. And note how it doesn't do the summer-time conversion.
|
* Check these. And note how it doesn't do the summer-time conversion.
|
||||||
*
|
*
|
||||||
|
134
diff.c
134
diff.c
@ -237,7 +237,7 @@ static char *pprint_rename(const char *a, const char *b)
|
|||||||
if (a_midlen < 0) a_midlen = 0;
|
if (a_midlen < 0) a_midlen = 0;
|
||||||
if (b_midlen < 0) b_midlen = 0;
|
if (b_midlen < 0) b_midlen = 0;
|
||||||
|
|
||||||
name = xmalloc(len_a + len_b - pfx_length - sfx_length + 7);
|
name = xmalloc(pfx_length + a_midlen + b_midlen + sfx_length + 7);
|
||||||
sprintf(name, "%.*s{%.*s => %.*s}%s",
|
sprintf(name, "%.*s{%.*s => %.*s}%s",
|
||||||
pfx_length, a,
|
pfx_length, a,
|
||||||
a_midlen, a + pfx_length,
|
a_midlen, a + pfx_length,
|
||||||
@ -299,6 +299,7 @@ static void diffstat_consume(void *priv, char *line, unsigned long len)
|
|||||||
|
|
||||||
static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
|
static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
|
||||||
static const char minuses[]= "----------------------------------------------------------------------";
|
static const char minuses[]= "----------------------------------------------------------------------";
|
||||||
|
const char mime_boundary_leader[] = "------------";
|
||||||
|
|
||||||
static void show_stats(struct diffstat_t* data)
|
static void show_stats(struct diffstat_t* data)
|
||||||
{
|
{
|
||||||
@ -397,6 +398,46 @@ static void show_stats(struct diffstat_t* data)
|
|||||||
total_files, adds, dels);
|
total_files, adds, dels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct checkdiff_t {
|
||||||
|
struct xdiff_emit_state xm;
|
||||||
|
const char *filename;
|
||||||
|
int lineno;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void checkdiff_consume(void *priv, char *line, unsigned long len)
|
||||||
|
{
|
||||||
|
struct checkdiff_t *data = priv;
|
||||||
|
|
||||||
|
if (line[0] == '+') {
|
||||||
|
int i, spaces = 0;
|
||||||
|
|
||||||
|
data->lineno++;
|
||||||
|
|
||||||
|
/* check space before tab */
|
||||||
|
for (i = 1; i < len && (line[i] == ' ' || line[i] == '\t'); i++)
|
||||||
|
if (line[i] == ' ')
|
||||||
|
spaces++;
|
||||||
|
if (line[i - 1] == '\t' && spaces)
|
||||||
|
printf("%s:%d: space before tab:%.*s\n",
|
||||||
|
data->filename, data->lineno, (int)len, line);
|
||||||
|
|
||||||
|
/* check white space at line end */
|
||||||
|
if (line[len - 1] == '\n')
|
||||||
|
len--;
|
||||||
|
if (isspace(line[len - 1]))
|
||||||
|
printf("%s:%d: white space at end: %.*s\n",
|
||||||
|
data->filename, data->lineno, (int)len, line);
|
||||||
|
} else if (line[0] == ' ')
|
||||||
|
data->lineno++;
|
||||||
|
else if (line[0] == '@') {
|
||||||
|
char *plus = strchr(line, '+');
|
||||||
|
if (plus)
|
||||||
|
data->lineno = strtol(plus, NULL, 10);
|
||||||
|
else
|
||||||
|
die("invalid diff");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned char *deflate_it(char *data,
|
static unsigned char *deflate_it(char *data,
|
||||||
unsigned long size,
|
unsigned long size,
|
||||||
unsigned long *result_size)
|
unsigned long *result_size)
|
||||||
@ -624,6 +665,41 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void builtin_checkdiff(const char *name_a, const char *name_b,
|
||||||
|
struct diff_filespec *one,
|
||||||
|
struct diff_filespec *two)
|
||||||
|
{
|
||||||
|
mmfile_t mf1, mf2;
|
||||||
|
struct checkdiff_t data;
|
||||||
|
|
||||||
|
if (!two)
|
||||||
|
return;
|
||||||
|
|
||||||
|
memset(&data, 0, sizeof(data));
|
||||||
|
data.xm.consume = checkdiff_consume;
|
||||||
|
data.filename = name_b ? name_b : name_a;
|
||||||
|
data.lineno = 0;
|
||||||
|
|
||||||
|
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
|
||||||
|
die("unable to read files to diff");
|
||||||
|
|
||||||
|
if (mmfile_is_binary(&mf2))
|
||||||
|
return;
|
||||||
|
else {
|
||||||
|
/* Crazy xdl interfaces.. */
|
||||||
|
xpparam_t xpp;
|
||||||
|
xdemitconf_t xecfg;
|
||||||
|
xdemitcb_t ecb;
|
||||||
|
|
||||||
|
xpp.flags = XDF_NEED_MINIMAL;
|
||||||
|
xecfg.ctxlen = 0;
|
||||||
|
xecfg.flags = 0;
|
||||||
|
ecb.outf = xdiff_outf;
|
||||||
|
ecb.priv = &data;
|
||||||
|
xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct diff_filespec *alloc_filespec(const char *path)
|
struct diff_filespec *alloc_filespec(const char *path)
|
||||||
{
|
{
|
||||||
int namelen = strlen(path);
|
int namelen = strlen(path);
|
||||||
@ -1180,6 +1256,25 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
|
|||||||
builtin_diffstat(name, other, p->one, p->two, diffstat, complete_rewrite);
|
builtin_diffstat(name, other, p->one, p->two, diffstat, complete_rewrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
const char *other;
|
||||||
|
|
||||||
|
if (DIFF_PAIR_UNMERGED(p)) {
|
||||||
|
/* unmerged */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = p->one->path;
|
||||||
|
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
|
||||||
|
|
||||||
|
diff_fill_sha1_info(p->one);
|
||||||
|
diff_fill_sha1_info(p->two);
|
||||||
|
|
||||||
|
builtin_checkdiff(name, other, p->one, p->two);
|
||||||
|
}
|
||||||
|
|
||||||
void diff_setup(struct diff_options *options)
|
void diff_setup(struct diff_options *options)
|
||||||
{
|
{
|
||||||
memset(options, 0, sizeof(*options));
|
memset(options, 0, sizeof(*options));
|
||||||
@ -1205,9 +1300,18 @@ int diff_setup_done(struct diff_options *options)
|
|||||||
* recursive bits for other formats here.
|
* recursive bits for other formats here.
|
||||||
*/
|
*/
|
||||||
if ((options->output_format == DIFF_FORMAT_PATCH) ||
|
if ((options->output_format == DIFF_FORMAT_PATCH) ||
|
||||||
(options->output_format == DIFF_FORMAT_DIFFSTAT))
|
(options->output_format == DIFF_FORMAT_DIFFSTAT) ||
|
||||||
|
(options->output_format == DIFF_FORMAT_CHECKDIFF))
|
||||||
options->recursive = 1;
|
options->recursive = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These combinations do not make sense.
|
||||||
|
*/
|
||||||
|
if (options->output_format == DIFF_FORMAT_RAW)
|
||||||
|
options->with_raw = 0;
|
||||||
|
if (options->output_format == DIFF_FORMAT_DIFFSTAT)
|
||||||
|
options->with_stat = 0;
|
||||||
|
|
||||||
if (options->detect_rename && options->rename_limit < 0)
|
if (options->detect_rename && options->rename_limit < 0)
|
||||||
options->rename_limit = diff_rename_limit_default;
|
options->rename_limit = diff_rename_limit_default;
|
||||||
if (options->setup & DIFF_SETUP_USE_CACHE) {
|
if (options->setup & DIFF_SETUP_USE_CACHE) {
|
||||||
@ -1288,6 +1392,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
|
|||||||
}
|
}
|
||||||
else if (!strcmp(arg, "--stat"))
|
else if (!strcmp(arg, "--stat"))
|
||||||
options->output_format = DIFF_FORMAT_DIFFSTAT;
|
options->output_format = DIFF_FORMAT_DIFFSTAT;
|
||||||
|
else if (!strcmp(arg, "--check"))
|
||||||
|
options->output_format = DIFF_FORMAT_CHECKDIFF;
|
||||||
else if (!strcmp(arg, "--summary"))
|
else if (!strcmp(arg, "--summary"))
|
||||||
options->summary = 1;
|
options->summary = 1;
|
||||||
else if (!strcmp(arg, "--patch-with-stat")) {
|
else if (!strcmp(arg, "--patch-with-stat")) {
|
||||||
@ -1610,6 +1716,19 @@ static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o,
|
|||||||
run_diffstat(p, o, diffstat);
|
run_diffstat(p, o, diffstat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void diff_flush_checkdiff(struct diff_filepair *p,
|
||||||
|
struct diff_options *o)
|
||||||
|
{
|
||||||
|
if (diff_unmodified_pair(p))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
|
||||||
|
(DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
|
||||||
|
return; /* no tree diffs in patch format */
|
||||||
|
|
||||||
|
run_checkdiff(p, o);
|
||||||
|
}
|
||||||
|
|
||||||
int diff_queue_is_empty(void)
|
int diff_queue_is_empty(void)
|
||||||
{
|
{
|
||||||
struct diff_queue_struct *q = &diff_queued_diff;
|
struct diff_queue_struct *q = &diff_queued_diff;
|
||||||
@ -1740,6 +1859,9 @@ static void flush_one_pair(struct diff_filepair *p,
|
|||||||
case DIFF_FORMAT_DIFFSTAT:
|
case DIFF_FORMAT_DIFFSTAT:
|
||||||
diff_flush_stat(p, options, diffstat);
|
diff_flush_stat(p, options, diffstat);
|
||||||
break;
|
break;
|
||||||
|
case DIFF_FORMAT_CHECKDIFF:
|
||||||
|
diff_flush_checkdiff(p, options);
|
||||||
|
break;
|
||||||
case DIFF_FORMAT_PATCH:
|
case DIFF_FORMAT_PATCH:
|
||||||
diff_flush_patch(p, options);
|
diff_flush_patch(p, options);
|
||||||
break;
|
break;
|
||||||
@ -1867,6 +1989,12 @@ void diff_flush(struct diff_options *options)
|
|||||||
show_stats(diffstat);
|
show_stats(diffstat);
|
||||||
free(diffstat);
|
free(diffstat);
|
||||||
diffstat = NULL;
|
diffstat = NULL;
|
||||||
|
if (options->summary)
|
||||||
|
for (i = 0; i < q->nr; i++)
|
||||||
|
diff_summary(q->queue[i]);
|
||||||
|
if (options->stat_sep)
|
||||||
|
fputs(options->stat_sep, stdout);
|
||||||
|
else
|
||||||
putchar(options->line_termination);
|
putchar(options->line_termination);
|
||||||
}
|
}
|
||||||
for (i = 0; i < q->nr; i++) {
|
for (i = 0; i < q->nr; i++) {
|
||||||
@ -1880,7 +2008,7 @@ void diff_flush(struct diff_options *options)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < q->nr; i++) {
|
for (i = 0; i < q->nr; i++) {
|
||||||
if (options->summary)
|
if (diffstat && options->summary)
|
||||||
diff_summary(q->queue[i]);
|
diff_summary(q->queue[i]);
|
||||||
diff_free_filepair(q->queue[i]);
|
diff_free_filepair(q->queue[i]);
|
||||||
}
|
}
|
||||||
|
4
diff.h
4
diff.h
@ -44,6 +44,7 @@ struct diff_options {
|
|||||||
int rename_limit;
|
int rename_limit;
|
||||||
int setup;
|
int setup;
|
||||||
int abbrev;
|
int abbrev;
|
||||||
|
const char *stat_sep;
|
||||||
|
|
||||||
int nr_paths;
|
int nr_paths;
|
||||||
const char **paths;
|
const char **paths;
|
||||||
@ -52,6 +53,8 @@ struct diff_options {
|
|||||||
add_remove_fn_t add_remove;
|
add_remove_fn_t add_remove;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern const char mime_boundary_leader[];
|
||||||
|
|
||||||
extern void diff_tree_setup_paths(const char **paths, struct diff_options *);
|
extern void diff_tree_setup_paths(const char **paths, struct diff_options *);
|
||||||
extern void diff_tree_release_paths(struct diff_options *);
|
extern void diff_tree_release_paths(struct diff_options *);
|
||||||
extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
|
extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
|
||||||
@ -153,6 +156,7 @@ extern int diff_queue_is_empty(void);
|
|||||||
#define DIFF_FORMAT_NAME 4
|
#define DIFF_FORMAT_NAME 4
|
||||||
#define DIFF_FORMAT_NAME_STATUS 5
|
#define DIFF_FORMAT_NAME_STATUS 5
|
||||||
#define DIFF_FORMAT_DIFFSTAT 6
|
#define DIFF_FORMAT_DIFFSTAT 6
|
||||||
|
#define DIFF_FORMAT_CHECKDIFF 7
|
||||||
|
|
||||||
extern void diff_flush(struct diff_options*);
|
extern void diff_flush(struct diff_options*);
|
||||||
|
|
||||||
|
64
fetch-pack.c
64
fetch-pack.c
@ -262,22 +262,58 @@ static void mark_recent_complete_commits(unsigned long cutoff)
|
|||||||
|
|
||||||
static void filter_refs(struct ref **refs, int nr_match, char **match)
|
static void filter_refs(struct ref **refs, int nr_match, char **match)
|
||||||
{
|
{
|
||||||
struct ref *prev, *current, *next;
|
struct ref **return_refs;
|
||||||
|
struct ref *newlist = NULL;
|
||||||
|
struct ref **newtail = &newlist;
|
||||||
|
struct ref *ref, *next;
|
||||||
|
struct ref *fastarray[32];
|
||||||
|
|
||||||
for (prev = NULL, current = *refs; current; current = next) {
|
if (nr_match && !fetch_all) {
|
||||||
next = current->next;
|
if (ARRAY_SIZE(fastarray) < nr_match)
|
||||||
if ((!memcmp(current->name, "refs/", 5) &&
|
return_refs = xcalloc(nr_match, sizeof(struct ref *));
|
||||||
check_ref_format(current->name + 5)) ||
|
else {
|
||||||
(!fetch_all &&
|
return_refs = fastarray;
|
||||||
!path_match(current->name, nr_match, match))) {
|
memset(return_refs, 0, sizeof(struct ref *) * nr_match);
|
||||||
if (prev == NULL)
|
|
||||||
*refs = next;
|
|
||||||
else
|
|
||||||
prev->next = next;
|
|
||||||
free(current);
|
|
||||||
} else
|
|
||||||
prev = current;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return_refs = NULL;
|
||||||
|
|
||||||
|
for (ref = *refs; ref; ref = next) {
|
||||||
|
next = ref->next;
|
||||||
|
if (!memcmp(ref->name, "refs/", 5) &&
|
||||||
|
check_ref_format(ref->name + 5))
|
||||||
|
; /* trash */
|
||||||
|
else if (fetch_all) {
|
||||||
|
*newtail = ref;
|
||||||
|
ref->next = NULL;
|
||||||
|
newtail = &ref->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int order = path_match(ref->name, nr_match, match);
|
||||||
|
if (order) {
|
||||||
|
return_refs[order-1] = ref;
|
||||||
|
continue; /* we will link it later */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fetch_all) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < nr_match; i++) {
|
||||||
|
ref = return_refs[i];
|
||||||
|
if (ref) {
|
||||||
|
*newtail = ref;
|
||||||
|
ref->next = NULL;
|
||||||
|
newtail = &ref->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (return_refs != fastarray)
|
||||||
|
free(return_refs);
|
||||||
|
}
|
||||||
|
*refs = newlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int everything_local(struct ref **refs, int nr_match, char **match)
|
static int everything_local(struct ref **refs, int nr_match, char **match)
|
||||||
|
34
fetch.c
34
fetch.c
@ -9,8 +9,6 @@
|
|||||||
|
|
||||||
const char *write_ref = NULL;
|
const char *write_ref = NULL;
|
||||||
|
|
||||||
const unsigned char *current_ref = NULL;
|
|
||||||
|
|
||||||
int get_tree = 0;
|
int get_tree = 0;
|
||||||
int get_history = 0;
|
int get_history = 0;
|
||||||
int get_all = 0;
|
int get_all = 0;
|
||||||
@ -43,16 +41,22 @@ static int process_tree(struct tree *tree)
|
|||||||
if (parse_tree(tree))
|
if (parse_tree(tree))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
entry = tree->entries;
|
entry = create_tree_entry_list(tree);
|
||||||
tree->entries = NULL;
|
|
||||||
while (entry) {
|
while (entry) {
|
||||||
struct tree_entry_list *next = entry->next;
|
struct tree_entry_list *next = entry->next;
|
||||||
if (process(entry->item.any))
|
|
||||||
return -1;
|
if (entry->directory) {
|
||||||
free(entry->name);
|
struct tree *tree = lookup_tree(entry->sha1);
|
||||||
|
process_tree(tree);
|
||||||
|
} else {
|
||||||
|
struct blob *blob = lookup_blob(entry->sha1);
|
||||||
|
process(&blob->object);
|
||||||
|
}
|
||||||
free(entry);
|
free(entry);
|
||||||
entry = next;
|
entry = next;
|
||||||
}
|
}
|
||||||
|
free(tree->buffer);
|
||||||
|
tree->buffer = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,19 +209,12 @@ static int mark_complete(const char *path, const unsigned char *sha1)
|
|||||||
int pull(char *target)
|
int pull(char *target)
|
||||||
{
|
{
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
int fd = -1;
|
|
||||||
|
|
||||||
save_commit_buffer = 0;
|
save_commit_buffer = 0;
|
||||||
track_object_refs = 0;
|
track_object_refs = 0;
|
||||||
if (write_ref && current_ref) {
|
|
||||||
fd = lock_ref_sha1(write_ref, current_ref);
|
|
||||||
if (fd < 0)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!get_recover) {
|
if (!get_recover)
|
||||||
for_each_ref(mark_complete);
|
for_each_ref(mark_complete);
|
||||||
}
|
|
||||||
|
|
||||||
if (interpret_target(target, sha1))
|
if (interpret_target(target, sha1))
|
||||||
return error("Could not interpret %s as something to pull",
|
return error("Could not interpret %s as something to pull",
|
||||||
@ -227,12 +224,7 @@ int pull(char *target)
|
|||||||
if (loop())
|
if (loop())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (write_ref) {
|
if (write_ref)
|
||||||
if (current_ref) {
|
|
||||||
write_ref_sha1(write_ref, fd, sha1);
|
|
||||||
} else {
|
|
||||||
write_ref_sha1_unlocked(write_ref, sha1);
|
write_ref_sha1_unlocked(write_ref, sha1);
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
3
fetch.h
3
fetch.h
@ -25,9 +25,6 @@ extern int fetch_ref(char *ref, unsigned char *sha1);
|
|||||||
/* If set, the ref filename to write the target value to. */
|
/* If set, the ref filename to write the target value to. */
|
||||||
extern const char *write_ref;
|
extern const char *write_ref;
|
||||||
|
|
||||||
/* If set, the hash that the current value of write_ref must be. */
|
|
||||||
extern const unsigned char *current_ref;
|
|
||||||
|
|
||||||
/* Set to fetch the target tree. */
|
/* Set to fetch the target tree. */
|
||||||
extern int get_tree;
|
extern int get_tree;
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "cache-tree.h"
|
#include "cache-tree.h"
|
||||||
|
|
||||||
#define REACHABLE 0x0001
|
#define REACHABLE 0x0001
|
||||||
|
#define SEEN 0x0002
|
||||||
|
|
||||||
static int show_root = 0;
|
static int show_root = 0;
|
||||||
static int show_tags = 0;
|
static int show_tags = 0;
|
||||||
@ -161,7 +162,7 @@ static int fsck_tree(struct tree *item)
|
|||||||
struct tree_entry_list *entry, *last;
|
struct tree_entry_list *entry, *last;
|
||||||
|
|
||||||
last = NULL;
|
last = NULL;
|
||||||
for (entry = item->entries; entry; entry = entry->next) {
|
for (entry = create_tree_entry_list(item); entry; entry = entry->next) {
|
||||||
if (strchr(entry->name, '/'))
|
if (strchr(entry->name, '/'))
|
||||||
has_full_path = 1;
|
has_full_path = 1;
|
||||||
has_zero_pad |= entry->zeropad;
|
has_zero_pad |= entry->zeropad;
|
||||||
@ -198,17 +199,15 @@ static int fsck_tree(struct tree *item)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
free(last->name);
|
|
||||||
free(last);
|
free(last);
|
||||||
}
|
}
|
||||||
|
|
||||||
last = entry;
|
last = entry;
|
||||||
}
|
}
|
||||||
if (last) {
|
if (last)
|
||||||
free(last->name);
|
|
||||||
free(last);
|
free(last);
|
||||||
}
|
free(item->buffer);
|
||||||
item->entries = NULL;
|
item->buffer = NULL;
|
||||||
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
if (has_full_path) {
|
if (has_full_path) {
|
||||||
@ -278,6 +277,9 @@ static int fsck_sha1(unsigned char *sha1)
|
|||||||
struct object *obj = parse_object(sha1);
|
struct object *obj = parse_object(sha1);
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return error("%s: object not found", sha1_to_hex(sha1));
|
return error("%s: object not found", sha1_to_hex(sha1));
|
||||||
|
if (obj->flags & SEEN)
|
||||||
|
return 0;
|
||||||
|
obj->flags |= SEEN;
|
||||||
if (obj->type == blob_type)
|
if (obj->type == blob_type)
|
||||||
return 0;
|
return 0;
|
||||||
if (obj->type == tree_type)
|
if (obj->type == tree_type)
|
||||||
@ -465,6 +467,7 @@ int main(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
int i, heads;
|
int i, heads;
|
||||||
|
|
||||||
|
track_object_refs = 1;
|
||||||
setup_git_directory();
|
setup_git_directory();
|
||||||
|
|
||||||
for (i = 1; i < argc; i++) {
|
for (i = 1; i < argc; i++) {
|
||||||
|
@ -37,7 +37,6 @@ show-branch
|
|||||||
status
|
status
|
||||||
tag
|
tag
|
||||||
verify-tag
|
verify-tag
|
||||||
whatchanged
|
|
||||||
EOF
|
EOF
|
||||||
while read cmd
|
while read cmd
|
||||||
do
|
do
|
||||||
|
14
git-clone.sh
14
git-clone.sh
@ -9,7 +9,7 @@
|
|||||||
unset CDPATH
|
unset CDPATH
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
echo >&2 "Usage: $0 [--use-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
|
echo >&2 "Usage: $0 [--template=<template_directory>] [--use-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,6 +102,7 @@ quiet=
|
|||||||
local=no
|
local=no
|
||||||
use_local=no
|
use_local=no
|
||||||
local_shared=no
|
local_shared=no
|
||||||
|
unset template
|
||||||
no_checkout=
|
no_checkout=
|
||||||
upload_pack=
|
upload_pack=
|
||||||
bare=
|
bare=
|
||||||
@ -120,6 +121,11 @@ while
|
|||||||
*,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) use_local=yes ;;
|
*,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) use_local=yes ;;
|
||||||
*,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared)
|
*,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared)
|
||||||
local_shared=yes; use_local=yes ;;
|
local_shared=yes; use_local=yes ;;
|
||||||
|
1,--template) usage ;;
|
||||||
|
*,--template)
|
||||||
|
shift; template="--template=$1" ;;
|
||||||
|
*,--template=*)
|
||||||
|
template="$1" ;;
|
||||||
*,-q|*,--quiet) quiet=-q ;;
|
*,-q|*,--quiet) quiet=-q ;;
|
||||||
*,--use-separate-remote)
|
*,--use-separate-remote)
|
||||||
use_separate_remote=t ;;
|
use_separate_remote=t ;;
|
||||||
@ -199,11 +205,11 @@ dir="$2"
|
|||||||
[ -e "$dir" ] && echo "$dir already exists." && usage
|
[ -e "$dir" ] && echo "$dir already exists." && usage
|
||||||
mkdir -p "$dir" &&
|
mkdir -p "$dir" &&
|
||||||
D=$(cd "$dir" && pwd) &&
|
D=$(cd "$dir" && pwd) &&
|
||||||
trap 'err=$?; cd ..; rm -r "$D"; exit $err' exit
|
trap 'err=$?; cd ..; rm -r "$D"; exit $err' 0
|
||||||
case "$bare" in
|
case "$bare" in
|
||||||
yes) GIT_DIR="$D" ;;
|
yes) GIT_DIR="$D" ;;
|
||||||
*) GIT_DIR="$D/.git" ;;
|
*) GIT_DIR="$D/.git" ;;
|
||||||
esac && export GIT_DIR && git-init-db || usage
|
esac && export GIT_DIR && git-init-db ${template+"$template"} || usage
|
||||||
case "$bare" in
|
case "$bare" in
|
||||||
yes)
|
yes)
|
||||||
GIT_DIR="$D" ;;
|
GIT_DIR="$D" ;;
|
||||||
@ -407,5 +413,5 @@ Pull: refs/heads/$head_points_at:$origin_track" &&
|
|||||||
fi
|
fi
|
||||||
rm -f "$GIT_DIR/CLONE_HEAD" "$GIT_DIR/REMOTE_HEAD"
|
rm -f "$GIT_DIR/CLONE_HEAD" "$GIT_DIR/REMOTE_HEAD"
|
||||||
|
|
||||||
trap - exit
|
trap - 0
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
# Copyright (c) 2005 Linus Torvalds
|
# Copyright (c) 2005 Linus Torvalds
|
||||||
# Copyright (c) 2006 Junio C Hamano
|
# Copyright (c) 2006 Junio C Hamano
|
||||||
|
|
||||||
USAGE='[-a] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit>) [--amend] [-e] [--author <author>] [[-i | -o] <path>...]'
|
USAGE='[-a] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit>] [-u] [--amend] [-e] [--author <author>] [[-i | -o] <path>...]'
|
||||||
SUBDIRECTORY_OK=Yes
|
SUBDIRECTORY_OK=Yes
|
||||||
. git-sh-setup
|
. git-sh-setup
|
||||||
|
|
||||||
@ -134,13 +134,17 @@ run_status () {
|
|||||||
report "Changed but not updated" \
|
report "Changed but not updated" \
|
||||||
"use git-update-index to mark for commit"
|
"use git-update-index to mark for commit"
|
||||||
|
|
||||||
|
option=""
|
||||||
|
if test -z "$untracked_files"; then
|
||||||
|
option="--directory --no-empty-directory"
|
||||||
|
fi
|
||||||
if test -f "$GIT_DIR/info/exclude"
|
if test -f "$GIT_DIR/info/exclude"
|
||||||
then
|
then
|
||||||
git-ls-files -z --others --directory \
|
git-ls-files -z --others $option \
|
||||||
--exclude-from="$GIT_DIR/info/exclude" \
|
--exclude-from="$GIT_DIR/info/exclude" \
|
||||||
--exclude-per-directory=.gitignore
|
--exclude-per-directory=.gitignore
|
||||||
else
|
else
|
||||||
git-ls-files -z --others --directory \
|
git-ls-files -z --others $option \
|
||||||
--exclude-per-directory=.gitignore
|
--exclude-per-directory=.gitignore
|
||||||
fi |
|
fi |
|
||||||
perl -e '$/ = "\0";
|
perl -e '$/ = "\0";
|
||||||
@ -203,6 +207,7 @@ verbose=
|
|||||||
signoff=
|
signoff=
|
||||||
force_author=
|
force_author=
|
||||||
only_include_assumed=
|
only_include_assumed=
|
||||||
|
untracked_files=
|
||||||
while case "$#" in 0) break;; esac
|
while case "$#" in 0) break;; esac
|
||||||
do
|
do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@ -340,6 +345,12 @@ do
|
|||||||
verbose=t
|
verbose=t
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
-u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|--untracked|\
|
||||||
|
--untracked-|--untracked-f|--untracked-fi|--untracked-fil|--untracked-file|\
|
||||||
|
--untracked-files)
|
||||||
|
untracked_files=t
|
||||||
|
shift
|
||||||
|
;;
|
||||||
--)
|
--)
|
||||||
shift
|
shift
|
||||||
break
|
break
|
||||||
@ -615,6 +626,9 @@ fi
|
|||||||
if test -z "$no_edit"
|
if test -z "$no_edit"
|
||||||
then
|
then
|
||||||
{
|
{
|
||||||
|
echo ""
|
||||||
|
echo "# Please enter the commit message for your changes."
|
||||||
|
echo "# (Comment lines starting with '#' will not be included)"
|
||||||
test -z "$only_include_assumed" || echo "$only_include_assumed"
|
test -z "$only_include_assumed" || echo "$only_include_assumed"
|
||||||
run_status
|
run_status
|
||||||
} >>"$GIT_DIR"/COMMIT_EDITMSG
|
} >>"$GIT_DIR"/COMMIT_EDITMSG
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
#!/usr/bin/perl -w
|
#!/usr/bin/perl -w
|
||||||
|
|
||||||
|
# Known limitations:
|
||||||
|
# - cannot add or remove binary files
|
||||||
|
# - does not propagate permissions
|
||||||
|
# - tells "ready for commit" even when things could not be completed
|
||||||
|
# (eg addition of a binary file)
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use Getopt::Std;
|
use Getopt::Std;
|
||||||
use File::Temp qw(tempdir);
|
use File::Temp qw(tempdir);
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
use File::Basename qw(basename);
|
use File::Basename qw(basename dirname);
|
||||||
|
|
||||||
unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
|
unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
|
||||||
die "GIT_DIR is not defined or is unreadable";
|
die "GIT_DIR is not defined or is unreadable";
|
||||||
@ -84,7 +90,7 @@ close MSG;
|
|||||||
`git-cat-file commit $commit | sed -e '1,/^\$/d' >> .msg`;
|
`git-cat-file commit $commit | sed -e '1,/^\$/d' >> .msg`;
|
||||||
$? && die "Error extracting the commit message";
|
$? && die "Error extracting the commit message";
|
||||||
|
|
||||||
my (@afiles, @dfiles, @mfiles);
|
my (@afiles, @dfiles, @mfiles, @dirs);
|
||||||
my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
|
my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
|
||||||
#print @files;
|
#print @files;
|
||||||
$? && die "Error in git-diff-tree";
|
$? && die "Error in git-diff-tree";
|
||||||
@ -92,7 +98,14 @@ foreach my $f (@files) {
|
|||||||
chomp $f;
|
chomp $f;
|
||||||
my @fields = split(m!\s+!, $f);
|
my @fields = split(m!\s+!, $f);
|
||||||
if ($fields[4] eq 'A') {
|
if ($fields[4] eq 'A') {
|
||||||
push @afiles, $fields[5];
|
my $path = $fields[5];
|
||||||
|
push @afiles, $path;
|
||||||
|
# add any needed parent directories
|
||||||
|
$path = dirname $path;
|
||||||
|
while (!-d $path and ! grep { $_ eq $path } @dirs) {
|
||||||
|
unshift @dirs, $path;
|
||||||
|
$path = dirname $path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($fields[4] eq 'M') {
|
if ($fields[4] eq 'M') {
|
||||||
push @mfiles, $fields[5];
|
push @mfiles, $fields[5];
|
||||||
@ -107,13 +120,21 @@ undef @files; # don't need it anymore
|
|||||||
|
|
||||||
# check that the files are clean and up to date according to cvs
|
# check that the files are clean and up to date according to cvs
|
||||||
my $dirty;
|
my $dirty;
|
||||||
|
foreach my $d (@dirs) {
|
||||||
|
if (-e $d) {
|
||||||
|
$dirty = 1;
|
||||||
|
warn "$d exists and is not a directory!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
foreach my $f (@afiles) {
|
foreach my $f (@afiles) {
|
||||||
# This should return only one value
|
# This should return only one value
|
||||||
my @status = grep(m/^File/, safe_pipe_capture('cvs', '-q', 'status' ,$f));
|
my @status = grep(m/^File/, safe_pipe_capture('cvs', '-q', 'status' ,$f));
|
||||||
if (@status > 1) { warn 'Strange! cvs status returned more than one line?'};
|
if (@status > 1) { warn 'Strange! cvs status returned more than one line?'};
|
||||||
unless ($status[0] =~ m/Status: Unknown$/) {
|
if (-d dirname $f and $status[0] !~ m/Status: Unknown$/
|
||||||
|
and $status[0] !~ m/^File: no file /) {
|
||||||
$dirty = 1;
|
$dirty = 1;
|
||||||
warn "File $f is already known in your CVS checkout -- perhaps it has been added by another user. Or this may indicate that it exists on a different branch. If this is the case, use -f to force the merge.\n";
|
warn "File $f is already known in your CVS checkout -- perhaps it has been added by another user. Or this may indicate that it exists on a different branch. If this is the case, use -f to force the merge.\n";
|
||||||
|
warn "Status was: $status\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach my $f (@mfiles, @dfiles) {
|
foreach my $f (@mfiles, @dfiles) {
|
||||||
@ -139,6 +160,19 @@ if ($dirty) {
|
|||||||
###
|
###
|
||||||
|
|
||||||
|
|
||||||
|
print "Creating new directories\n";
|
||||||
|
foreach my $d (@dirs) {
|
||||||
|
unless (mkdir $d) {
|
||||||
|
warn "Could not mkdir $d: $!";
|
||||||
|
$dirty = 1;
|
||||||
|
}
|
||||||
|
`cvs add $d`;
|
||||||
|
if ($?) {
|
||||||
|
$dirty = 1;
|
||||||
|
warn "Failed to cvs add directory $d -- you may need to do it manually";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
print "'Patching' binary files\n";
|
print "'Patching' binary files\n";
|
||||||
|
|
||||||
my @bfiles = grep(m/^Binary/, safe_pipe_capture('git-diff-tree', '-p', $parent, $commit));
|
my @bfiles = grep(m/^Binary/, safe_pipe_capture('git-diff-tree', '-p', $parent, $commit));
|
||||||
@ -151,7 +185,7 @@ foreach my $f (@bfiles) {
|
|||||||
my $blob = `git-ls-tree $tree "$f" | cut -f 1 | cut -d ' ' -f 3`;
|
my $blob = `git-ls-tree $tree "$f" | cut -f 1 | cut -d ' ' -f 3`;
|
||||||
chomp $blob;
|
chomp $blob;
|
||||||
`git-cat-file blob $blob > $tmpdir/blob`;
|
`git-cat-file blob $blob > $tmpdir/blob`;
|
||||||
if (system('cmp', '-q', $f, "$tmpdir/blob")) {
|
if (system('cmp', '-s', $f, "$tmpdir/blob")) {
|
||||||
warn "Binary file $f in CVS does not match parent.\n";
|
warn "Binary file $f in CVS does not match parent.\n";
|
||||||
$dirty = 1;
|
$dirty = 1;
|
||||||
next;
|
next;
|
||||||
|
@ -23,13 +23,13 @@ use File::Basename qw(basename dirname);
|
|||||||
use Time::Local;
|
use Time::Local;
|
||||||
use IO::Socket;
|
use IO::Socket;
|
||||||
use IO::Pipe;
|
use IO::Pipe;
|
||||||
use POSIX qw(strftime dup2);
|
use POSIX qw(strftime dup2 ENOENT);
|
||||||
use IPC::Open2;
|
use IPC::Open2;
|
||||||
|
|
||||||
$SIG{'PIPE'}="IGNORE";
|
$SIG{'PIPE'}="IGNORE";
|
||||||
$ENV{'TZ'}="UTC";
|
$ENV{'TZ'}="UTC";
|
||||||
|
|
||||||
our($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S);
|
our($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L);
|
||||||
my (%conv_author_name, %conv_author_email);
|
my (%conv_author_name, %conv_author_email);
|
||||||
|
|
||||||
sub usage() {
|
sub usage() {
|
||||||
@ -85,7 +85,7 @@ sub write_author_info($) {
|
|||||||
close ($f);
|
close ($f);
|
||||||
}
|
}
|
||||||
|
|
||||||
getopts("hivmkuo:d:p:C:z:s:M:P:A:S:") or usage();
|
getopts("hivmkuo:d:p:C:z:s:M:P:A:S:L:") or usage();
|
||||||
usage if $opt_h;
|
usage if $opt_h;
|
||||||
|
|
||||||
@ARGV <= 1 or usage();
|
@ARGV <= 1 or usage();
|
||||||
@ -315,15 +315,7 @@ sub _line {
|
|||||||
chomp $cnt;
|
chomp $cnt;
|
||||||
die "Duh: Filesize $cnt" if $cnt !~ /^\d+$/;
|
die "Duh: Filesize $cnt" if $cnt !~ /^\d+$/;
|
||||||
$line="";
|
$line="";
|
||||||
$res=0;
|
$res = $self->_fetchfile($fh, $cnt);
|
||||||
while($cnt) {
|
|
||||||
my $buf;
|
|
||||||
my $num = $self->{'socketi'}->read($buf,$cnt);
|
|
||||||
die "Server: Filesize $cnt: $num: $!\n" if not defined $num or $num<=0;
|
|
||||||
print $fh $buf;
|
|
||||||
$res += $num;
|
|
||||||
$cnt -= $num;
|
|
||||||
}
|
|
||||||
} elsif($line =~ s/^ //) {
|
} elsif($line =~ s/^ //) {
|
||||||
print $fh $line;
|
print $fh $line;
|
||||||
$res += length($line);
|
$res += length($line);
|
||||||
@ -335,14 +327,7 @@ sub _line {
|
|||||||
chomp $cnt;
|
chomp $cnt;
|
||||||
die "Duh: Mbinary $cnt" if $cnt !~ /^\d+$/ or $cnt<1;
|
die "Duh: Mbinary $cnt" if $cnt !~ /^\d+$/ or $cnt<1;
|
||||||
$line="";
|
$line="";
|
||||||
while($cnt) {
|
$res += $self->_fetchfile($fh, $cnt);
|
||||||
my $buf;
|
|
||||||
my $num = $self->{'socketi'}->read($buf,$cnt);
|
|
||||||
die "S: Mbinary $cnt: $num: $!\n" if not defined $num or $num<=0;
|
|
||||||
print $fh $buf;
|
|
||||||
$res += $num;
|
|
||||||
$cnt -= $num;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
chomp $line;
|
chomp $line;
|
||||||
if($line eq "ok") {
|
if($line eq "ok") {
|
||||||
@ -384,6 +369,23 @@ sub file {
|
|||||||
|
|
||||||
return ($name, $res);
|
return ($name, $res);
|
||||||
}
|
}
|
||||||
|
sub _fetchfile {
|
||||||
|
my ($self, $fh, $cnt) = @_;
|
||||||
|
my $res = 0;
|
||||||
|
my $bufsize = 1024 * 1024;
|
||||||
|
while($cnt) {
|
||||||
|
if ($bufsize > $cnt) {
|
||||||
|
$bufsize = $cnt;
|
||||||
|
}
|
||||||
|
my $buf;
|
||||||
|
my $num = $self->{'socketi'}->read($buf,$bufsize);
|
||||||
|
die "Server: Filesize $cnt: $num: $!\n" if not defined $num or $num<=0;
|
||||||
|
print $fh $buf;
|
||||||
|
$res += $num;
|
||||||
|
$cnt -= $num;
|
||||||
|
}
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
package main;
|
package main;
|
||||||
@ -429,21 +431,24 @@ sub getwd() {
|
|||||||
return $pwd;
|
return $pwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub is_sha1 {
|
||||||
sub get_headref($$) {
|
my $s = shift;
|
||||||
my $name = shift;
|
return $s =~ /^[a-f0-9]{40}$/;
|
||||||
my $git_dir = shift;
|
|
||||||
my $sha;
|
|
||||||
|
|
||||||
if (open(C,"$git_dir/refs/heads/$name")) {
|
|
||||||
chomp($sha = <C>);
|
|
||||||
close(C);
|
|
||||||
length($sha) == 40
|
|
||||||
or die "Cannot get head id for $name ($sha): $!\n";
|
|
||||||
}
|
|
||||||
return $sha;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub get_headref ($$) {
|
||||||
|
my $name = shift;
|
||||||
|
my $git_dir = shift;
|
||||||
|
|
||||||
|
my $f = "$git_dir/refs/heads/$name";
|
||||||
|
if(open(my $fh, $f)) {
|
||||||
|
chomp(my $r = <$fh>);
|
||||||
|
is_sha1($r) or die "Cannot get head id for $name ($r): $!";
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
die "unable to open $f: $!" unless $! == POSIX::ENOENT;
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
-d $git_tree
|
-d $git_tree
|
||||||
or mkdir($git_tree,0777)
|
or mkdir($git_tree,0777)
|
||||||
@ -561,98 +566,66 @@ unless($pid) {
|
|||||||
|
|
||||||
my $state = 0;
|
my $state = 0;
|
||||||
|
|
||||||
my($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg);
|
sub update_index (\@\@) {
|
||||||
my(@old,@new,@skipped);
|
my $old = shift;
|
||||||
my $commit = sub {
|
my $new = shift;
|
||||||
my $pid;
|
open(my $fh, '|-', qw(git-update-index -z --index-info))
|
||||||
while(@old) {
|
or die "unable to open git-update-index: $!";
|
||||||
my @o2;
|
print $fh
|
||||||
if(@old > 55) {
|
(map { "0 0000000000000000000000000000000000000000\t$_\0" }
|
||||||
@o2 = splice(@old,0,50);
|
@$old),
|
||||||
} else {
|
(map { '100' . sprintf('%o', $_->[0]) . " $_->[1]\t$_->[2]\0" }
|
||||||
@o2 = @old;
|
@$new)
|
||||||
@old = ();
|
or die "unable to write to git-update-index: $!";
|
||||||
}
|
close $fh
|
||||||
system("git-update-index","--force-remove","--",@o2);
|
or die "unable to write to git-update-index: $!";
|
||||||
die "Cannot remove files: $?\n" if $?;
|
$? and die "git-update-index reported error: $?";
|
||||||
}
|
}
|
||||||
while(@new) {
|
|
||||||
my @n2;
|
|
||||||
if(@new > 12) {
|
|
||||||
@n2 = splice(@new,0,10);
|
|
||||||
} else {
|
|
||||||
@n2 = @new;
|
|
||||||
@new = ();
|
|
||||||
}
|
|
||||||
system("git-update-index","--add",
|
|
||||||
(map { ('--cacheinfo', @$_) } @n2));
|
|
||||||
die "Cannot add files: $?\n" if $?;
|
|
||||||
}
|
|
||||||
|
|
||||||
$pid = open(C,"-|");
|
sub write_tree () {
|
||||||
die "Cannot fork: $!" unless defined $pid;
|
open(my $fh, '-|', qw(git-write-tree))
|
||||||
unless($pid) {
|
or die "unable to open git-write-tree: $!";
|
||||||
exec("git-write-tree");
|
chomp(my $tree = <$fh>);
|
||||||
die "Cannot exec git-write-tree: $!\n";
|
is_sha1($tree)
|
||||||
}
|
or die "Cannot get tree id ($tree): $!";
|
||||||
chomp(my $tree = <C>);
|
close($fh)
|
||||||
length($tree) == 40
|
|
||||||
or die "Cannot get tree id ($tree): $!\n";
|
|
||||||
close(C)
|
|
||||||
or die "Error running git-write-tree: $?\n";
|
or die "Error running git-write-tree: $?\n";
|
||||||
print "Tree ID $tree\n" if $opt_v;
|
print "Tree ID $tree\n" if $opt_v;
|
||||||
|
return $tree;
|
||||||
|
}
|
||||||
|
|
||||||
my $parent = "";
|
my($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg);
|
||||||
if(open(C,"$git_dir/refs/heads/$last_branch")) {
|
my(@old,@new,@skipped);
|
||||||
chomp($parent = <C>);
|
sub commit {
|
||||||
close(C);
|
update_index(@old, @new);
|
||||||
length($parent) == 40
|
@old = @new = ();
|
||||||
or die "Cannot get parent id ($parent): $!\n";
|
my $tree = write_tree();
|
||||||
print "Parent ID $parent\n" if $opt_v;
|
my $parent = get_headref($last_branch, $git_dir);
|
||||||
}
|
print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v;
|
||||||
|
|
||||||
my $pr = IO::Pipe->new() or die "Cannot open pipe: $!\n";
|
my @commit_args;
|
||||||
my $pw = IO::Pipe->new() or die "Cannot open pipe: $!\n";
|
push @commit_args, ("-p", $parent) if $parent;
|
||||||
$pid = fork();
|
|
||||||
die "Fork: $!\n" unless defined $pid;
|
|
||||||
unless($pid) {
|
|
||||||
$pr->writer();
|
|
||||||
$pw->reader();
|
|
||||||
open(OUT,">&STDOUT");
|
|
||||||
dup2($pw->fileno(),0);
|
|
||||||
dup2($pr->fileno(),1);
|
|
||||||
$pr->close();
|
|
||||||
$pw->close();
|
|
||||||
|
|
||||||
my @par = ();
|
|
||||||
@par = ("-p",$parent) if $parent;
|
|
||||||
|
|
||||||
# loose detection of merges
|
# loose detection of merges
|
||||||
# based on the commit msg
|
# based on the commit msg
|
||||||
foreach my $rx (@mergerx) {
|
foreach my $rx (@mergerx) {
|
||||||
if ($logmsg =~ $rx) {
|
next unless $logmsg =~ $rx && $1;
|
||||||
my $mparent = $1;
|
my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
|
||||||
if ($mparent eq 'HEAD') { $mparent = $opt_o };
|
if(my $sha1 = get_headref($mparent, $git_dir)) {
|
||||||
if ( -e "$git_dir/refs/heads/$mparent") {
|
push @commit_args, '-p', $mparent;
|
||||||
$mparent = get_headref($mparent, $git_dir);
|
print "Merge parent branch: $mparent\n" if $opt_v;
|
||||||
push @par, '-p', $mparent;
|
|
||||||
print OUT "Merge parent branch: $mparent\n" if $opt_v;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exec("env",
|
my $commit_date = strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date));
|
||||||
"GIT_AUTHOR_NAME=$author_name",
|
$ENV{GIT_AUTHOR_NAME} = $author_name;
|
||||||
"GIT_AUTHOR_EMAIL=$author_email",
|
$ENV{GIT_AUTHOR_EMAIL} = $author_email;
|
||||||
"GIT_AUTHOR_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
|
$ENV{GIT_AUTHOR_DATE} = $commit_date;
|
||||||
"GIT_COMMITTER_NAME=$author_name",
|
$ENV{GIT_COMMITTER_NAME} = $author_name;
|
||||||
"GIT_COMMITTER_EMAIL=$author_email",
|
$ENV{GIT_COMMITTER_EMAIL} = $author_email;
|
||||||
"GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
|
$ENV{GIT_COMMITTER_DATE} = $commit_date;
|
||||||
"git-commit-tree", $tree,@par);
|
my $pid = open2(my $commit_read, my $commit_write,
|
||||||
die "Cannot exec git-commit-tree: $!\n";
|
'git-commit-tree', $tree, @commit_args);
|
||||||
}
|
|
||||||
$pw->writer();
|
|
||||||
$pr->reader();
|
|
||||||
|
|
||||||
# compatibility with git2cvs
|
# compatibility with git2cvs
|
||||||
substr($logmsg,32767) = "" if length($logmsg) > 32767;
|
substr($logmsg,32767) = "" if length($logmsg) > 32767;
|
||||||
@ -661,18 +634,17 @@ my $commit = sub {
|
|||||||
if (@skipped) {
|
if (@skipped) {
|
||||||
$logmsg .= "\n\n\nSKIPPED:\n\t";
|
$logmsg .= "\n\n\nSKIPPED:\n\t";
|
||||||
$logmsg .= join("\n\t", @skipped) . "\n";
|
$logmsg .= join("\n\t", @skipped) . "\n";
|
||||||
|
@skipped = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
print $pw "$logmsg\n"
|
print($commit_write "$logmsg\n") && close($commit_write)
|
||||||
or die "Error writing to git-commit-tree: $!\n";
|
or die "Error writing to git-commit-tree: $!\n";
|
||||||
$pw->close();
|
|
||||||
|
|
||||||
print "Committed patch $patchset ($branch ".strftime("%Y-%m-%d %H:%M:%S",gmtime($date)).")\n" if $opt_v;
|
print "Committed patch $patchset ($branch $commit_date)\n" if $opt_v;
|
||||||
chomp(my $cid = <$pr>);
|
chomp(my $cid = <$commit_read>);
|
||||||
length($cid) == 40
|
is_sha1($cid) or die "Cannot get commit id ($cid): $!\n";
|
||||||
or die "Cannot get commit id ($cid): $!\n";
|
|
||||||
print "Commit ID $cid\n" if $opt_v;
|
print "Commit ID $cid\n" if $opt_v;
|
||||||
$pr->close();
|
close($commit_read);
|
||||||
|
|
||||||
waitpid($pid,0);
|
waitpid($pid,0);
|
||||||
die "Error running git-commit-tree: $?\n" if $?;
|
die "Error running git-commit-tree: $?\n" if $?;
|
||||||
@ -716,6 +688,7 @@ my $commit = sub {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
my $commitcount = 1;
|
||||||
while(<CVS>) {
|
while(<CVS>) {
|
||||||
chomp;
|
chomp;
|
||||||
if($state == 0 and /^-+$/) {
|
if($state == 0 and /^-+$/) {
|
||||||
@ -849,7 +822,14 @@ while(<CVS>) {
|
|||||||
} elsif($state == 9 and /^\s*$/) {
|
} elsif($state == 9 and /^\s*$/) {
|
||||||
$state = 10;
|
$state = 10;
|
||||||
} elsif(($state == 9 or $state == 10) and /^-+$/) {
|
} elsif(($state == 9 or $state == 10) and /^-+$/) {
|
||||||
&$commit();
|
$commitcount++;
|
||||||
|
if ($opt_L && $commitcount > $opt_L) {
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
commit();
|
||||||
|
if (($commitcount & 1023) == 0) {
|
||||||
|
system("git repack -a -d");
|
||||||
|
}
|
||||||
$state = 1;
|
$state = 1;
|
||||||
} elsif($state == 11 and /^-+$/) {
|
} elsif($state == 11 and /^-+$/) {
|
||||||
$state = 1;
|
$state = 1;
|
||||||
@ -859,7 +839,7 @@ while(<CVS>) {
|
|||||||
print "* UNKNOWN LINE * $_\n";
|
print "* UNKNOWN LINE * $_\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&$commit() if $branch and $state != 11;
|
commit() if $branch and $state != 11;
|
||||||
|
|
||||||
unlink($git_index);
|
unlink($git_index);
|
||||||
|
|
||||||
|
@ -211,12 +211,12 @@ esac
|
|||||||
reflist=$(get_remote_refs_for_fetch "$@")
|
reflist=$(get_remote_refs_for_fetch "$@")
|
||||||
if test "$tags"
|
if test "$tags"
|
||||||
then
|
then
|
||||||
taglist=$(IFS=" " &&
|
taglist=`IFS=" " &&
|
||||||
git-ls-remote $upload_pack --tags "$remote" |
|
git-ls-remote $upload_pack --tags "$remote" |
|
||||||
while read sha1 name
|
while read sha1 name
|
||||||
do
|
do
|
||||||
case "$name" in
|
case "$name" in
|
||||||
(*^*) continue ;;
|
*^*) continue ;;
|
||||||
esac
|
esac
|
||||||
if git-check-ref-format "$name"
|
if git-check-ref-format "$name"
|
||||||
then
|
then
|
||||||
@ -224,7 +224,7 @@ then
|
|||||||
else
|
else
|
||||||
echo >&2 "warning: tag ${name} ignored"
|
echo >&2 "warning: tag ${name} ignored"
|
||||||
fi
|
fi
|
||||||
done)
|
done`
|
||||||
if test "$#" -gt 1
|
if test "$#" -gt 1
|
||||||
then
|
then
|
||||||
# remote URL plus explicit refspecs; we need to merge them.
|
# remote URL plus explicit refspecs; we need to merge them.
|
||||||
|
@ -1,344 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# Copyright (c) 2005 Junio C Hamano
|
|
||||||
#
|
|
||||||
|
|
||||||
USAGE='[-n | -k] [-o <dir> | --stdout] [--signoff] [--check] [--diff-options] [--attach] <his> [<mine>]'
|
|
||||||
LONG_USAGE='Prepare each commit with its patch since <mine> head forked from
|
|
||||||
<his> head, one file per patch formatted to resemble UNIX mailbox
|
|
||||||
format, for e-mail submission or use with git-am.
|
|
||||||
|
|
||||||
Each output file is numbered sequentially from 1, and uses the
|
|
||||||
first line of the commit message (massaged for pathname safety)
|
|
||||||
as the filename.
|
|
||||||
|
|
||||||
When -o is specified, output files are created in <dir>; otherwise
|
|
||||||
they are created in the current working directory. This option
|
|
||||||
is ignored if --stdout is specified.
|
|
||||||
|
|
||||||
When -n is specified, instead of "[PATCH] Subject", the first
|
|
||||||
line is formatted as "[PATCH N/M] Subject", unless you have only
|
|
||||||
one patch.
|
|
||||||
|
|
||||||
When --attach is specified, patches are attached, not inlined.'
|
|
||||||
|
|
||||||
. git-sh-setup
|
|
||||||
|
|
||||||
# Force diff to run in C locale.
|
|
||||||
LANG=C LC_ALL=C
|
|
||||||
export LANG LC_ALL
|
|
||||||
|
|
||||||
diff_opts=
|
|
||||||
LF='
|
|
||||||
'
|
|
||||||
|
|
||||||
outdir=./
|
|
||||||
while case "$#" in 0) break;; esac
|
|
||||||
do
|
|
||||||
case "$1" in
|
|
||||||
-c|--c|--ch|--che|--chec|--check)
|
|
||||||
check=t ;;
|
|
||||||
-a|--a|--au|--aut|--auth|--autho|--author|\
|
|
||||||
-d|--d|--da|--dat|--date|\
|
|
||||||
-m|--m|--mb|--mbo|--mbox) # now noop
|
|
||||||
;;
|
|
||||||
--at|--att|--atta|--attac|--attach)
|
|
||||||
attach=t ;;
|
|
||||||
-k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\
|
|
||||||
--keep-subj|--keep-subje|--keep-subjec|--keep-subject)
|
|
||||||
keep_subject=t ;;
|
|
||||||
-n|--n|--nu|--num|--numb|--numbe|--number|--numbere|--numbered)
|
|
||||||
numbered=t ;;
|
|
||||||
-s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
|
|
||||||
signoff=t ;;
|
|
||||||
--st|--std|--stdo|--stdou|--stdout)
|
|
||||||
stdout=t ;;
|
|
||||||
-o=*|--o=*|--ou=*|--out=*|--outp=*|--outpu=*|--output=*|--output-=*|\
|
|
||||||
--output-d=*|--output-di=*|--output-dir=*|--output-dire=*|\
|
|
||||||
--output-direc=*|--output-direct=*|--output-directo=*|\
|
|
||||||
--output-director=*|--output-directory=*)
|
|
||||||
outdir=`expr "$1" : '-[^=]*=\(.*\)'` ;;
|
|
||||||
-o|--o|--ou|--out|--outp|--outpu|--output|--output-|--output-d|\
|
|
||||||
--output-di|--output-dir|--output-dire|--output-direc|--output-direct|\
|
|
||||||
--output-directo|--output-director|--output-directory)
|
|
||||||
case "$#" in 1) usage ;; esac; shift
|
|
||||||
outdir="$1" ;;
|
|
||||||
-h|--h|--he|--hel|--help)
|
|
||||||
usage
|
|
||||||
;;
|
|
||||||
-*' '* | -*"$LF"* | -*' '*)
|
|
||||||
# Ignore diff option that has whitespace for now.
|
|
||||||
;;
|
|
||||||
-*) diff_opts="$diff_opts$1 " ;;
|
|
||||||
*) break ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
case "$keep_subject$numbered" in
|
|
||||||
tt)
|
|
||||||
die '--keep-subject and --numbered are incompatible.' ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
tmp=.tmp-series$$
|
|
||||||
trap 'rm -f $tmp-*' 0 1 2 3 15
|
|
||||||
|
|
||||||
series=$tmp-series
|
|
||||||
commsg=$tmp-commsg
|
|
||||||
filelist=$tmp-files
|
|
||||||
|
|
||||||
# Backward compatible argument parsing hack.
|
|
||||||
#
|
|
||||||
# Historically, we supported:
|
|
||||||
# 1. "rev1" is equivalent to "rev1..HEAD"
|
|
||||||
# 2. "rev1..rev2"
|
|
||||||
# 3. "rev1" "rev2 is equivalent to "rev1..rev2"
|
|
||||||
#
|
|
||||||
# We want to take a sequence of "rev1..rev2" in general.
|
|
||||||
# Also, "rev1.." should mean "rev1..HEAD"; git-diff users are
|
|
||||||
# familiar with that syntax.
|
|
||||||
|
|
||||||
case "$#,$1$2" in
|
|
||||||
1,?*..?*)
|
|
||||||
# single "rev1..rev2"
|
|
||||||
;;
|
|
||||||
1,?*..)
|
|
||||||
# single "rev1.." should mean "rev1..HEAD"
|
|
||||||
set x "$1"HEAD
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
1,*)
|
|
||||||
# single rev1
|
|
||||||
set x "$1..HEAD"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
2,?*..?*)
|
|
||||||
# not traditional "rev1" "rev2"
|
|
||||||
;;
|
|
||||||
2,*)
|
|
||||||
set x "$1..$2"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Now we have what we want in $@
|
|
||||||
for revpair
|
|
||||||
do
|
|
||||||
case "$revpair" in
|
|
||||||
?*..?*)
|
|
||||||
rev1=`expr "z$revpair" : 'z\(.*\)\.\.'`
|
|
||||||
rev2=`expr "z$revpair" : 'z.*\.\.\(.*\)'`
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
rev1="$revpair^"
|
|
||||||
rev2="$revpair"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
git-rev-parse --verify "$rev1^0" >/dev/null 2>&1 ||
|
|
||||||
die "Not a valid rev $rev1 ($revpair)"
|
|
||||||
git-rev-parse --verify "$rev2^0" >/dev/null 2>&1 ||
|
|
||||||
die "Not a valid rev $rev2 ($revpair)"
|
|
||||||
git-cherry -v "$rev1" "$rev2" |
|
|
||||||
while read sign rev comment
|
|
||||||
do
|
|
||||||
case "$sign" in
|
|
||||||
'-')
|
|
||||||
echo >&2 "Merged already: $comment"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo $rev
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
done >$series
|
|
||||||
|
|
||||||
me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'`
|
|
||||||
headers=`git-repo-config --get format.headers`
|
|
||||||
case "$attach" in
|
|
||||||
"") ;;
|
|
||||||
*)
|
|
||||||
mimemagic="050802040500080604070107"
|
|
||||||
esac
|
|
||||||
|
|
||||||
case "$outdir" in
|
|
||||||
*/) ;;
|
|
||||||
*) outdir="$outdir/" ;;
|
|
||||||
esac
|
|
||||||
test -d "$outdir" || mkdir -p "$outdir" || exit
|
|
||||||
|
|
||||||
titleScript='
|
|
||||||
/./d
|
|
||||||
/^$/n
|
|
||||||
s/^\[PATCH[^]]*\] *//
|
|
||||||
s/[^-a-z.A-Z_0-9]/-/g
|
|
||||||
s/\.\.\.*/\./g
|
|
||||||
s/\.*$//
|
|
||||||
s/--*/-/g
|
|
||||||
s/^-//
|
|
||||||
s/-$//
|
|
||||||
s/$/./
|
|
||||||
p
|
|
||||||
q
|
|
||||||
'
|
|
||||||
|
|
||||||
process_one () {
|
|
||||||
perl -w -e '
|
|
||||||
my ($keep_subject, $num, $signoff, $headers, $mimemagic, $commsg) = @ARGV;
|
|
||||||
my ($signoff_pattern, $done_header, $done_subject, $done_separator, $signoff_seen,
|
|
||||||
$last_was_signoff);
|
|
||||||
|
|
||||||
if ($signoff) {
|
|
||||||
$signoff = "Signed-off-by: " . `git-var GIT_COMMITTER_IDENT`;
|
|
||||||
$signoff =~ s/>.*/>/;
|
|
||||||
$signoff_pattern = quotemeta($signoff);
|
|
||||||
}
|
|
||||||
|
|
||||||
my @weekday_names = qw(Sun Mon Tue Wed Thu Fri Sat);
|
|
||||||
my @month_names = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
|
|
||||||
|
|
||||||
sub show_date {
|
|
||||||
my ($time, $tz) = @_;
|
|
||||||
my $minutes = abs($tz);
|
|
||||||
$minutes = int($minutes / 100) * 60 + ($minutes % 100);
|
|
||||||
if ($tz < 0) {
|
|
||||||
$minutes = -$minutes;
|
|
||||||
}
|
|
||||||
my $t = $time + $minutes * 60;
|
|
||||||
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($t);
|
|
||||||
return sprintf("%s, %d %s %d %02d:%02d:%02d %+05d",
|
|
||||||
$weekday_names[$wday], $mday,
|
|
||||||
$month_names[$mon], $year+1900,
|
|
||||||
$hour, $min, $sec, $tz);
|
|
||||||
}
|
|
||||||
|
|
||||||
print "From nobody Mon Sep 17 00:00:00 2001\n";
|
|
||||||
open FH, "git stripspace <$commsg |" or die "open $commsg pipe";
|
|
||||||
while (<FH>) {
|
|
||||||
unless ($done_header) {
|
|
||||||
if (/^$/) {
|
|
||||||
$done_header = 1;
|
|
||||||
}
|
|
||||||
elsif (/^author (.*>) (.*)$/) {
|
|
||||||
my ($author_ident, $author_date) = ($1, $2);
|
|
||||||
my ($utc, $off) = ($author_date =~ /^(\d+) ([-+]?\d+)$/);
|
|
||||||
$author_date = show_date($utc, $off);
|
|
||||||
|
|
||||||
print "From: $author_ident\n";
|
|
||||||
print "Date: $author_date\n";
|
|
||||||
}
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
unless ($done_subject) {
|
|
||||||
unless ($keep_subject) {
|
|
||||||
s/^\[PATCH[^]]*\]\s*//;
|
|
||||||
s/^/[PATCH$num] /;
|
|
||||||
}
|
|
||||||
if ($headers) {
|
|
||||||
print "$headers\n";
|
|
||||||
}
|
|
||||||
print "Subject: $_";
|
|
||||||
if ($mimemagic) {
|
|
||||||
print "MIME-Version: 1.0\n";
|
|
||||||
print "Content-Type: multipart/mixed;\n";
|
|
||||||
print " boundary=\"------------$mimemagic\"\n";
|
|
||||||
print "\n";
|
|
||||||
print "This is a multi-part message in MIME format.\n";
|
|
||||||
print "--------------$mimemagic\n";
|
|
||||||
print "Content-Type: text/plain; charset=UTF-8; format=fixed\n";
|
|
||||||
print "Content-Transfer-Encoding: 8bit\n";
|
|
||||||
}
|
|
||||||
$done_subject = 1;
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
unless ($done_separator) {
|
|
||||||
print "\n";
|
|
||||||
$done_separator = 1;
|
|
||||||
next if (/^$/);
|
|
||||||
}
|
|
||||||
|
|
||||||
$last_was_signoff = 0;
|
|
||||||
if (/Signed-off-by:/i) {
|
|
||||||
if ($signoff ne "" && /Signed-off-by:\s*$signoff_pattern$/i) {
|
|
||||||
$signoff_seen = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
print $_;
|
|
||||||
}
|
|
||||||
if (!$signoff_seen && $signoff ne "") {
|
|
||||||
if (!$last_was_signoff) {
|
|
||||||
print "\n";
|
|
||||||
}
|
|
||||||
print "$signoff\n";
|
|
||||||
}
|
|
||||||
print "\n---\n\n";
|
|
||||||
close FH or die "close $commsg pipe";
|
|
||||||
' "$keep_subject" "$num" "$signoff" "$headers" "$mimemagic" $commsg
|
|
||||||
|
|
||||||
git-diff-tree -p --stat --summary $diff_opts "$commit"
|
|
||||||
echo
|
|
||||||
case "$mimemagic" in
|
|
||||||
'');;
|
|
||||||
*)
|
|
||||||
echo "--------------$mimemagic"
|
|
||||||
echo "Content-Type: text/x-patch;"
|
|
||||||
echo " name=\"$commit.diff\""
|
|
||||||
echo "Content-Transfer-Encoding: 8bit"
|
|
||||||
echo "Content-Disposition: inline;"
|
|
||||||
echo " filename=\"$commit.diff\""
|
|
||||||
echo
|
|
||||||
esac
|
|
||||||
git-diff-tree -p $diff_opts "$commit"
|
|
||||||
case "$mimemagic" in
|
|
||||||
'')
|
|
||||||
echo "-- "
|
|
||||||
echo "@@GIT_VERSION@@"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo
|
|
||||||
echo "--------------$mimemagic--"
|
|
||||||
echo
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
echo
|
|
||||||
}
|
|
||||||
|
|
||||||
total=`wc -l <$series | tr -dc "[0-9]"`
|
|
||||||
case "$total,$numbered" in
|
|
||||||
1,*)
|
|
||||||
numfmt='' ;;
|
|
||||||
*,t)
|
|
||||||
numfmt=`echo "$total" | wc -c`
|
|
||||||
numfmt=$(($numfmt-1))
|
|
||||||
numfmt=" %0${numfmt}d/$total"
|
|
||||||
esac
|
|
||||||
|
|
||||||
i=1
|
|
||||||
while read commit
|
|
||||||
do
|
|
||||||
git-cat-file commit "$commit" | git-stripspace >$commsg
|
|
||||||
title=`sed -ne "$titleScript" <$commsg`
|
|
||||||
case "$numbered" in
|
|
||||||
'') num= ;;
|
|
||||||
*)
|
|
||||||
num=`printf "$numfmt" $i` ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
file=`printf '%04d-%stxt' $i "$title"`
|
|
||||||
if test '' = "$stdout"
|
|
||||||
then
|
|
||||||
echo "$file"
|
|
||||||
process_one >"$outdir$file"
|
|
||||||
if test t = "$check"
|
|
||||||
then
|
|
||||||
# This is slightly modified from Andrew Morton's Perfect Patch.
|
|
||||||
# Lines you introduce should not have trailing whitespace.
|
|
||||||
# Also check for an indentation that has SP before a TAB.
|
|
||||||
grep -n '^+\([ ]* .*\|.*[ ]\)$' "$outdir$file"
|
|
||||||
:
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo >&2 "$file"
|
|
||||||
process_one
|
|
||||||
fi
|
|
||||||
i=`expr "$i" + 1`
|
|
||||||
done <$series
|
|
@ -58,11 +58,19 @@ http://* | https://* )
|
|||||||
;;
|
;;
|
||||||
|
|
||||||
rsync://* )
|
rsync://* )
|
||||||
mkdir $tmpdir
|
mkdir $tmpdir &&
|
||||||
|
rsync -rlq "$peek_repo/HEAD" $tmpdir &&
|
||||||
rsync -rq "$peek_repo/refs" $tmpdir || {
|
rsync -rq "$peek_repo/refs" $tmpdir || {
|
||||||
echo "failed slurping"
|
echo "failed slurping"
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
head=$(cat "$tmpdir/HEAD") &&
|
||||||
|
case "$head" in
|
||||||
|
ref:' '*)
|
||||||
|
head=$(expr "z$head" : 'zref: \(.*\)') &&
|
||||||
|
head=$(cat "$tmpdir/$head") || exit
|
||||||
|
esac &&
|
||||||
|
echo "$head HEAD"
|
||||||
(cd $tmpdir && find refs -type f) |
|
(cd $tmpdir && find refs -type f) |
|
||||||
while read path
|
while read path
|
||||||
do
|
do
|
||||||
|
@ -152,6 +152,6 @@ then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
git-format-patch -k --stdout --full-index "$upstream" ORIG_HEAD |
|
git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
|
||||||
git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
|
git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
|
||||||
|
|
||||||
|
17
git.c
17
git.c
@ -47,6 +47,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
|
|||||||
{ "whatchanged", cmd_whatchanged },
|
{ "whatchanged", cmd_whatchanged },
|
||||||
{ "show", cmd_show },
|
{ "show", cmd_show },
|
||||||
{ "push", cmd_push },
|
{ "push", cmd_push },
|
||||||
|
{ "format-patch", cmd_format_patch },
|
||||||
{ "count-objects", cmd_count_objects },
|
{ "count-objects", cmd_count_objects },
|
||||||
{ "diff", cmd_diff },
|
{ "diff", cmd_diff },
|
||||||
{ "grep", cmd_grep },
|
{ "grep", cmd_grep },
|
||||||
@ -54,7 +55,21 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
|
|||||||
{ "add", cmd_add },
|
{ "add", cmd_add },
|
||||||
{ "rev-list", cmd_rev_list },
|
{ "rev-list", cmd_rev_list },
|
||||||
{ "init-db", cmd_init_db },
|
{ "init-db", cmd_init_db },
|
||||||
{ "check-ref-format", cmd_check_ref_format }
|
{ "tar-tree", cmd_tar_tree },
|
||||||
|
{ "upload-tar", cmd_upload_tar },
|
||||||
|
{ "check-ref-format", cmd_check_ref_format },
|
||||||
|
{ "ls-files", cmd_ls_files },
|
||||||
|
{ "ls-tree", cmd_ls_tree },
|
||||||
|
{ "tar-tree", cmd_tar_tree },
|
||||||
|
{ "read-tree", cmd_read_tree },
|
||||||
|
{ "commit-tree", cmd_commit_tree },
|
||||||
|
{ "apply", cmd_apply },
|
||||||
|
{ "show-branch", cmd_show_branch },
|
||||||
|
{ "diff-files", cmd_diff_files },
|
||||||
|
{ "diff-index", cmd_diff_index },
|
||||||
|
{ "diff-stages", cmd_diff_stages },
|
||||||
|
{ "diff-tree", cmd_diff_tree },
|
||||||
|
{ "cat-file", cmd_cat_file }
|
||||||
};
|
};
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
30
http-push.c
30
http-push.c
@ -1704,6 +1704,7 @@ static struct object_list **process_blob(struct blob *blob,
|
|||||||
return p;
|
return p;
|
||||||
|
|
||||||
obj->flags |= SEEN;
|
obj->flags |= SEEN;
|
||||||
|
name = strdup(name);
|
||||||
return add_object(obj, p, path, name);
|
return add_object(obj, p, path, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1713,7 +1714,7 @@ static struct object_list **process_tree(struct tree *tree,
|
|||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
struct object *obj = &tree->object;
|
struct object *obj = &tree->object;
|
||||||
struct tree_entry_list *entry;
|
struct tree_desc desc;
|
||||||
struct name_path me;
|
struct name_path me;
|
||||||
|
|
||||||
obj->flags |= LOCAL;
|
obj->flags |= LOCAL;
|
||||||
@ -1724,21 +1725,30 @@ static struct object_list **process_tree(struct tree *tree,
|
|||||||
die("bad tree object %s", sha1_to_hex(obj->sha1));
|
die("bad tree object %s", sha1_to_hex(obj->sha1));
|
||||||
|
|
||||||
obj->flags |= SEEN;
|
obj->flags |= SEEN;
|
||||||
|
name = strdup(name);
|
||||||
p = add_object(obj, p, NULL, name);
|
p = add_object(obj, p, NULL, name);
|
||||||
me.up = path;
|
me.up = path;
|
||||||
me.elem = name;
|
me.elem = name;
|
||||||
me.elem_len = strlen(name);
|
me.elem_len = strlen(name);
|
||||||
entry = tree->entries;
|
|
||||||
tree->entries = NULL;
|
desc.buf = tree->buffer;
|
||||||
while (entry) {
|
desc.size = tree->size;
|
||||||
struct tree_entry_list *next = entry->next;
|
|
||||||
if (entry->directory)
|
while (desc.size) {
|
||||||
p = process_tree(entry->item.tree, p, &me, entry->name);
|
unsigned mode;
|
||||||
|
const char *name;
|
||||||
|
const unsigned char *sha1;
|
||||||
|
|
||||||
|
sha1 = tree_entry_extract(&desc, &name, &mode);
|
||||||
|
update_tree_entry(&desc);
|
||||||
|
|
||||||
|
if (S_ISDIR(mode))
|
||||||
|
p = process_tree(lookup_tree(sha1), p, &me, name);
|
||||||
else
|
else
|
||||||
p = process_blob(entry->item.blob, p, &me, entry->name);
|
p = process_blob(lookup_blob(sha1), p, &me, name);
|
||||||
free(entry);
|
|
||||||
entry = next;
|
|
||||||
}
|
}
|
||||||
|
free(tree->buffer);
|
||||||
|
tree->buffer = NULL;
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
60
log-tree.c
60
log-tree.c
@ -20,6 +20,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
|
|||||||
int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
|
int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
|
||||||
const char *extra;
|
const char *extra;
|
||||||
int len;
|
int len;
|
||||||
|
char *subject = NULL, *after_subject = NULL;
|
||||||
|
|
||||||
opt->loginfo = NULL;
|
opt->loginfo = NULL;
|
||||||
if (!opt->verbose_header) {
|
if (!opt->verbose_header) {
|
||||||
@ -49,19 +50,67 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
|
|||||||
/*
|
/*
|
||||||
* Print header line of header..
|
* Print header line of header..
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
if (opt->commit_format == CMIT_FMT_EMAIL) {
|
||||||
|
char *sha1 = sha1_to_hex(commit->object.sha1);
|
||||||
|
if (opt->total > 0) {
|
||||||
|
static char buffer[64];
|
||||||
|
snprintf(buffer, sizeof(buffer),
|
||||||
|
"Subject: [PATCH %d/%d] ",
|
||||||
|
opt->nr, opt->total);
|
||||||
|
subject = buffer;
|
||||||
|
} else if (opt->total == 0)
|
||||||
|
subject = "Subject: [PATCH] ";
|
||||||
|
else
|
||||||
|
subject = "Subject: ";
|
||||||
|
|
||||||
|
printf("From %s Mon Sep 17 00:00:00 2001\n", sha1);
|
||||||
|
if (opt->mime_boundary) {
|
||||||
|
static char subject_buffer[1024];
|
||||||
|
static char buffer[1024];
|
||||||
|
snprintf(subject_buffer, sizeof(subject_buffer) - 1,
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: multipart/mixed;\n"
|
||||||
|
" boundary=\"%s%s\"\n"
|
||||||
|
"\n"
|
||||||
|
"This is a multi-part message in MIME "
|
||||||
|
"format.\n"
|
||||||
|
"--%s%s\n"
|
||||||
|
"Content-Type: text/plain; "
|
||||||
|
"charset=UTF-8; format=fixed\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n\n",
|
||||||
|
mime_boundary_leader, opt->mime_boundary,
|
||||||
|
mime_boundary_leader, opt->mime_boundary);
|
||||||
|
after_subject = subject_buffer;
|
||||||
|
|
||||||
|
snprintf(buffer, sizeof(buffer) - 1,
|
||||||
|
"--%s%s\n"
|
||||||
|
"Content-Type: text/x-patch;\n"
|
||||||
|
" name=\"%s.diff\"\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Content-Disposition: inline;\n"
|
||||||
|
" filename=\"%s.diff\"\n\n",
|
||||||
|
mime_boundary_leader, opt->mime_boundary,
|
||||||
|
sha1, sha1);
|
||||||
|
opt->diffopt.stat_sep = buffer;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
printf("%s%s",
|
printf("%s%s",
|
||||||
opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
|
opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
|
||||||
diff_unique_abbrev(commit->object.sha1, abbrev_commit));
|
diff_unique_abbrev(commit->object.sha1, abbrev_commit));
|
||||||
if (opt->parents)
|
if (opt->parents)
|
||||||
show_parents(commit, abbrev_commit);
|
show_parents(commit, abbrev_commit);
|
||||||
if (parent)
|
if (parent)
|
||||||
printf(" (from %s)", diff_unique_abbrev(parent->object.sha1, abbrev_commit));
|
printf(" (from %s)",
|
||||||
|
diff_unique_abbrev(parent->object.sha1,
|
||||||
|
abbrev_commit));
|
||||||
putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
|
putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* And then the pretty-printed message itself
|
* And then the pretty-printed message itself
|
||||||
*/
|
*/
|
||||||
len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev);
|
len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject, after_subject);
|
||||||
printf("%s%s%s", this_header, extra, sep);
|
printf("%s%s%s", this_header, extra, sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,15 +215,18 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
|
|||||||
int log_tree_commit(struct rev_info *opt, struct commit *commit)
|
int log_tree_commit(struct rev_info *opt, struct commit *commit)
|
||||||
{
|
{
|
||||||
struct log_info log;
|
struct log_info log;
|
||||||
|
int shown;
|
||||||
|
|
||||||
log.commit = commit;
|
log.commit = commit;
|
||||||
log.parent = NULL;
|
log.parent = NULL;
|
||||||
opt->loginfo = &log;
|
opt->loginfo = &log;
|
||||||
|
|
||||||
if (!log_tree_diff(opt, commit, &log) && opt->loginfo && opt->always_show_header) {
|
shown = log_tree_diff(opt, commit, &log);
|
||||||
|
if (!shown && opt->loginfo && opt->always_show_header) {
|
||||||
log.parent = NULL;
|
log.parent = NULL;
|
||||||
show_log(opt, opt->loginfo, "");
|
show_log(opt, opt->loginfo, "");
|
||||||
|
shown = 1;
|
||||||
}
|
}
|
||||||
opt->loginfo = NULL;
|
opt->loginfo = NULL;
|
||||||
return 0;
|
return shown;
|
||||||
}
|
}
|
||||||
|
150
mailinfo.c
150
mailinfo.c
@ -72,11 +72,14 @@ static int bogus_from(char *line)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int handle_from(char *line)
|
static int handle_from(char *in_line)
|
||||||
{
|
{
|
||||||
char *at = strchr(line, '@');
|
char line[1000];
|
||||||
|
char *at;
|
||||||
char *dst;
|
char *dst;
|
||||||
|
|
||||||
|
strcpy(line, in_line);
|
||||||
|
at = strchr(line, '@');
|
||||||
if (!at)
|
if (!at)
|
||||||
return bogus_from(line);
|
return bogus_from(line);
|
||||||
|
|
||||||
@ -237,38 +240,46 @@ static int eatspace(char *line)
|
|||||||
#define SEEN_FROM 01
|
#define SEEN_FROM 01
|
||||||
#define SEEN_DATE 02
|
#define SEEN_DATE 02
|
||||||
#define SEEN_SUBJECT 04
|
#define SEEN_SUBJECT 04
|
||||||
|
#define SEEN_BOGUS_UNIX_FROM 010
|
||||||
|
#define SEEN_PREFIX 020
|
||||||
|
|
||||||
/* First lines of body can have From:, Date:, and Subject: */
|
/* First lines of body can have From:, Date:, and Subject: */
|
||||||
static int handle_inbody_header(int *seen, char *line)
|
static void handle_inbody_header(int *seen, char *line)
|
||||||
{
|
{
|
||||||
|
if (!memcmp(">From", line, 5) && isspace(line[5])) {
|
||||||
|
if (!(*seen & SEEN_BOGUS_UNIX_FROM)) {
|
||||||
|
*seen |= SEEN_BOGUS_UNIX_FROM;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!memcmp("From:", line, 5) && isspace(line[5])) {
|
if (!memcmp("From:", line, 5) && isspace(line[5])) {
|
||||||
if (!(*seen & SEEN_FROM) && handle_from(line+6)) {
|
if (!(*seen & SEEN_FROM) && handle_from(line+6)) {
|
||||||
*seen |= SEEN_FROM;
|
*seen |= SEEN_FROM;
|
||||||
return 1;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!memcmp("Date:", line, 5) && isspace(line[5])) {
|
if (!memcmp("Date:", line, 5) && isspace(line[5])) {
|
||||||
if (!(*seen & SEEN_DATE)) {
|
if (!(*seen & SEEN_DATE)) {
|
||||||
handle_date(line+6);
|
handle_date(line+6);
|
||||||
*seen |= SEEN_DATE;
|
*seen |= SEEN_DATE;
|
||||||
return 1;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!memcmp("Subject:", line, 8) && isspace(line[8])) {
|
if (!memcmp("Subject:", line, 8) && isspace(line[8])) {
|
||||||
if (!(*seen & SEEN_SUBJECT)) {
|
if (!(*seen & SEEN_SUBJECT)) {
|
||||||
handle_subject(line+9);
|
handle_subject(line+9);
|
||||||
*seen |= SEEN_SUBJECT;
|
*seen |= SEEN_SUBJECT;
|
||||||
return 1;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
|
if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
|
||||||
if (!(*seen & SEEN_SUBJECT)) {
|
if (!(*seen & SEEN_SUBJECT)) {
|
||||||
handle_subject(line);
|
handle_subject(line);
|
||||||
*seen |= SEEN_SUBJECT;
|
*seen |= SEEN_SUBJECT;
|
||||||
return 1;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
*seen |= SEEN_PREFIX;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *cleanup_subject(char *subject)
|
static char *cleanup_subject(char *subject)
|
||||||
@ -324,6 +335,7 @@ static void cleanup_space(char *buf)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void decode_header_bq(char *it);
|
||||||
typedef int (*header_fn_t)(char *);
|
typedef int (*header_fn_t)(char *);
|
||||||
struct header_def {
|
struct header_def {
|
||||||
const char *name;
|
const char *name;
|
||||||
@ -331,7 +343,7 @@ struct header_def {
|
|||||||
int namelen;
|
int namelen;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void check_header(char *line, int len, struct header_def *header)
|
static void check_header(char *line, struct header_def *header)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -343,13 +355,17 @@ static void check_header(char *line, int len, struct header_def *header)
|
|||||||
int len = header[i].namelen;
|
int len = header[i].namelen;
|
||||||
if (!strncasecmp(line, header[i].name, len) &&
|
if (!strncasecmp(line, header[i].name, len) &&
|
||||||
line[len] == ':' && isspace(line[len + 1])) {
|
line[len] == ':' && isspace(line[len + 1])) {
|
||||||
|
/* Unwrap inline B and Q encoding, and optionally
|
||||||
|
* normalize the meta information to utf8.
|
||||||
|
*/
|
||||||
|
decode_header_bq(line + len + 2);
|
||||||
header[i].func(line + len + 2);
|
header[i].func(line + len + 2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void check_subheader_line(char *line, int len)
|
static void check_subheader_line(char *line)
|
||||||
{
|
{
|
||||||
static struct header_def header[] = {
|
static struct header_def header[] = {
|
||||||
{ "Content-Type", handle_subcontent_type },
|
{ "Content-Type", handle_subcontent_type },
|
||||||
@ -357,9 +373,9 @@ static void check_subheader_line(char *line, int len)
|
|||||||
handle_content_transfer_encoding },
|
handle_content_transfer_encoding },
|
||||||
{ NULL },
|
{ NULL },
|
||||||
};
|
};
|
||||||
check_header(line, len, header);
|
check_header(line, header);
|
||||||
}
|
}
|
||||||
static void check_header_line(char *line, int len)
|
static void check_header_line(char *line)
|
||||||
{
|
{
|
||||||
static struct header_def header[] = {
|
static struct header_def header[] = {
|
||||||
{ "From", handle_from },
|
{ "From", handle_from },
|
||||||
@ -370,7 +386,30 @@ static void check_header_line(char *line, int len)
|
|||||||
handle_content_transfer_encoding },
|
handle_content_transfer_encoding },
|
||||||
{ NULL },
|
{ NULL },
|
||||||
};
|
};
|
||||||
check_header(line, len, header);
|
check_header(line, header);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_rfc2822_header(char *line)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The section that defines the loosest possible
|
||||||
|
* field name is "3.6.8 Optional fields".
|
||||||
|
*
|
||||||
|
* optional-field = field-name ":" unstructured CRLF
|
||||||
|
* field-name = 1*ftext
|
||||||
|
* ftext = %d33-57 / %59-126
|
||||||
|
*/
|
||||||
|
int ch;
|
||||||
|
char *cp = line;
|
||||||
|
while ((ch = *cp++)) {
|
||||||
|
if (ch == ':')
|
||||||
|
return cp != line;
|
||||||
|
if ((33 <= ch && ch <= 57) ||
|
||||||
|
(59 <= ch && ch <= 126))
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_one_header_line(char *line, int sz, FILE *in)
|
static int read_one_header_line(char *line, int sz, FILE *in)
|
||||||
@ -379,18 +418,25 @@ static int read_one_header_line(char *line, int sz, FILE *in)
|
|||||||
while (ofs < sz) {
|
while (ofs < sz) {
|
||||||
int peek, len;
|
int peek, len;
|
||||||
if (fgets(line + ofs, sz - ofs, in) == NULL)
|
if (fgets(line + ofs, sz - ofs, in) == NULL)
|
||||||
return ofs;
|
break;
|
||||||
len = eatspace(line + ofs);
|
len = eatspace(line + ofs);
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
return ofs;
|
break;
|
||||||
peek = fgetc(in); ungetc(peek, in);
|
if (!is_rfc2822_header(line)) {
|
||||||
if (peek == ' ' || peek == '\t') {
|
/* Re-add the newline */
|
||||||
/* Yuck, 2822 header "folding" */
|
line[ofs + len] = '\n';
|
||||||
|
line[ofs + len + 1] = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
ofs += len;
|
ofs += len;
|
||||||
continue;
|
/* Yuck, 2822 header "folding" */
|
||||||
}
|
peek = fgetc(in); ungetc(peek, in);
|
||||||
return ofs + len;
|
if (peek != ' ' && peek != '\t')
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
/* Count mbox From headers as headers */
|
||||||
|
if (!ofs && !memcmp(line, "From ", 5))
|
||||||
|
ofs = 1;
|
||||||
return ofs;
|
return ofs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -585,25 +631,13 @@ static void decode_transfer_encoding(char *line)
|
|||||||
static void handle_info(void)
|
static void handle_info(void)
|
||||||
{
|
{
|
||||||
char *sub;
|
char *sub;
|
||||||
static int done_info = 0;
|
|
||||||
|
|
||||||
if (done_info)
|
|
||||||
return;
|
|
||||||
|
|
||||||
done_info = 1;
|
|
||||||
sub = cleanup_subject(subject);
|
sub = cleanup_subject(subject);
|
||||||
cleanup_space(name);
|
cleanup_space(name);
|
||||||
cleanup_space(date);
|
cleanup_space(date);
|
||||||
cleanup_space(email);
|
cleanup_space(email);
|
||||||
cleanup_space(sub);
|
cleanup_space(sub);
|
||||||
|
|
||||||
/* Unwrap inline B and Q encoding, and optionally
|
|
||||||
* normalize the meta information to utf8.
|
|
||||||
*/
|
|
||||||
decode_header_bq(name);
|
|
||||||
decode_header_bq(date);
|
|
||||||
decode_header_bq(email);
|
|
||||||
decode_header_bq(sub);
|
|
||||||
printf("Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n",
|
printf("Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n",
|
||||||
name, email, sub, date);
|
name, email, sub, date);
|
||||||
}
|
}
|
||||||
@ -611,7 +645,7 @@ static void handle_info(void)
|
|||||||
/* We are inside message body and have read line[] already.
|
/* We are inside message body and have read line[] already.
|
||||||
* Spit out the commit log.
|
* Spit out the commit log.
|
||||||
*/
|
*/
|
||||||
static int handle_commit_msg(void)
|
static int handle_commit_msg(int *seen)
|
||||||
{
|
{
|
||||||
if (!cmitmsg)
|
if (!cmitmsg)
|
||||||
return 0;
|
return 0;
|
||||||
@ -635,6 +669,11 @@ static int handle_commit_msg(void)
|
|||||||
decode_transfer_encoding(line);
|
decode_transfer_encoding(line);
|
||||||
if (metainfo_charset)
|
if (metainfo_charset)
|
||||||
convert_to_utf8(line, charset);
|
convert_to_utf8(line, charset);
|
||||||
|
|
||||||
|
handle_inbody_header(seen, line);
|
||||||
|
if (!(*seen & SEEN_PREFIX))
|
||||||
|
continue;
|
||||||
|
|
||||||
fputs(line, cmitmsg);
|
fputs(line, cmitmsg);
|
||||||
} while (fgets(line, sizeof(line), stdin) != NULL);
|
} while (fgets(line, sizeof(line), stdin) != NULL);
|
||||||
fclose(cmitmsg);
|
fclose(cmitmsg);
|
||||||
@ -666,26 +705,16 @@ static void handle_patch(void)
|
|||||||
* that the first part to contain commit message and a patch, and
|
* that the first part to contain commit message and a patch, and
|
||||||
* handle other parts as pure patches.
|
* handle other parts as pure patches.
|
||||||
*/
|
*/
|
||||||
static int handle_multipart_one_part(void)
|
static int handle_multipart_one_part(int *seen)
|
||||||
{
|
{
|
||||||
int seen = 0;
|
|
||||||
int n = 0;
|
int n = 0;
|
||||||
int len;
|
|
||||||
|
|
||||||
while (fgets(line, sizeof(line), stdin) != NULL) {
|
while (fgets(line, sizeof(line), stdin) != NULL) {
|
||||||
again:
|
again:
|
||||||
len = eatspace(line);
|
|
||||||
n++;
|
n++;
|
||||||
if (!len)
|
|
||||||
continue;
|
|
||||||
if (is_multipart_boundary(line))
|
if (is_multipart_boundary(line))
|
||||||
break;
|
break;
|
||||||
if (0 <= seen && handle_inbody_header(&seen, line))
|
if (handle_commit_msg(seen))
|
||||||
continue;
|
|
||||||
seen = -1; /* no more inbody headers */
|
|
||||||
line[len] = '\n';
|
|
||||||
handle_info();
|
|
||||||
if (handle_commit_msg())
|
|
||||||
goto again;
|
goto again;
|
||||||
handle_patch();
|
handle_patch();
|
||||||
break;
|
break;
|
||||||
@ -697,6 +726,7 @@ static int handle_multipart_one_part(void)
|
|||||||
|
|
||||||
static void handle_multipart_body(void)
|
static void handle_multipart_body(void)
|
||||||
{
|
{
|
||||||
|
int seen = 0;
|
||||||
int part_num = 0;
|
int part_num = 0;
|
||||||
|
|
||||||
/* Skip up to the first boundary */
|
/* Skip up to the first boundary */
|
||||||
@ -709,16 +739,16 @@ static void handle_multipart_body(void)
|
|||||||
return;
|
return;
|
||||||
/* We are on boundary line. Start slurping the subhead. */
|
/* We are on boundary line. Start slurping the subhead. */
|
||||||
while (1) {
|
while (1) {
|
||||||
int len = read_one_header_line(line, sizeof(line), stdin);
|
int hdr = read_one_header_line(line, sizeof(line), stdin);
|
||||||
if (!len) {
|
if (!hdr) {
|
||||||
if (handle_multipart_one_part() < 0)
|
if (handle_multipart_one_part(&seen) < 0)
|
||||||
return;
|
return;
|
||||||
/* Reset per part headers */
|
/* Reset per part headers */
|
||||||
transfer_encoding = TE_DONTCARE;
|
transfer_encoding = TE_DONTCARE;
|
||||||
charset[0] = 0;
|
charset[0] = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
check_subheader_line(line, len);
|
check_subheader_line(line);
|
||||||
}
|
}
|
||||||
fclose(patchfile);
|
fclose(patchfile);
|
||||||
if (!patch_lines) {
|
if (!patch_lines) {
|
||||||
@ -732,18 +762,9 @@ static void handle_body(void)
|
|||||||
{
|
{
|
||||||
int seen = 0;
|
int seen = 0;
|
||||||
|
|
||||||
while (fgets(line, sizeof(line), stdin) != NULL) {
|
if (line[0] || fgets(line, sizeof(line), stdin) != NULL) {
|
||||||
int len = eatspace(line);
|
handle_commit_msg(&seen);
|
||||||
if (!len)
|
|
||||||
continue;
|
|
||||||
if (0 <= seen && handle_inbody_header(&seen, line))
|
|
||||||
continue;
|
|
||||||
seen = -1; /* no more inbody headers */
|
|
||||||
line[len] = '\n';
|
|
||||||
handle_info();
|
|
||||||
handle_commit_msg();
|
|
||||||
handle_patch();
|
handle_patch();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
fclose(patchfile);
|
fclose(patchfile);
|
||||||
if (!patch_lines) {
|
if (!patch_lines) {
|
||||||
@ -787,15 +808,16 @@ int main(int argc, char **argv)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
while (1) {
|
while (1) {
|
||||||
int len = read_one_header_line(line, sizeof(line), stdin);
|
int hdr = read_one_header_line(line, sizeof(line), stdin);
|
||||||
if (!len) {
|
if (!hdr) {
|
||||||
if (multipart_boundary[0])
|
if (multipart_boundary[0])
|
||||||
handle_multipart_body();
|
handle_multipart_body();
|
||||||
else
|
else
|
||||||
handle_body();
|
handle_body();
|
||||||
|
handle_info();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
check_header_line(line, len);
|
check_header_line(line);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
45
mktag.c
45
mktag.c
@ -45,42 +45,46 @@ static int verify_tag(char *buffer, unsigned long size)
|
|||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
const char *object, *type_line, *tag_line, *tagger_line;
|
const char *object, *type_line, *tag_line, *tagger_line;
|
||||||
|
|
||||||
if (size < 64 || size > MAXSIZE-1)
|
if (size < 64)
|
||||||
return -1;
|
return error("wanna fool me ? you obviously got the size wrong !\n");
|
||||||
|
|
||||||
buffer[size] = 0;
|
buffer[size] = 0;
|
||||||
|
|
||||||
/* Verify object line */
|
/* Verify object line */
|
||||||
object = buffer;
|
object = buffer;
|
||||||
if (memcmp(object, "object ", 7))
|
if (memcmp(object, "object ", 7))
|
||||||
return -1;
|
return error("char%d: does not start with \"object \"\n", 0);
|
||||||
|
|
||||||
if (get_sha1_hex(object + 7, sha1))
|
if (get_sha1_hex(object + 7, sha1))
|
||||||
return -1;
|
return error("char%d: could not get SHA1 hash\n", 7);
|
||||||
|
|
||||||
/* Verify type line */
|
/* Verify type line */
|
||||||
type_line = object + 48;
|
type_line = object + 48;
|
||||||
if (memcmp(type_line - 1, "\ntype ", 6))
|
if (memcmp(type_line - 1, "\ntype ", 6))
|
||||||
return -1;
|
return error("char%d: could not find \"\\ntype \"\n", 47);
|
||||||
|
|
||||||
/* Verify tag-line */
|
/* Verify tag-line */
|
||||||
tag_line = strchr(type_line, '\n');
|
tag_line = strchr(type_line, '\n');
|
||||||
if (!tag_line)
|
if (!tag_line)
|
||||||
return -1;
|
return error("char%td: could not find next \"\\n\"\n", type_line - buffer);
|
||||||
tag_line++;
|
tag_line++;
|
||||||
if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n')
|
if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n')
|
||||||
return -1;
|
return error("char%td: no \"tag \" found\n", tag_line - buffer);
|
||||||
|
|
||||||
/* Get the actual type */
|
/* Get the actual type */
|
||||||
typelen = tag_line - type_line - strlen("type \n");
|
typelen = tag_line - type_line - strlen("type \n");
|
||||||
if (typelen >= sizeof(type))
|
if (typelen >= sizeof(type))
|
||||||
return -1;
|
return error("char%td: type too long\n", type_line+5 - buffer);
|
||||||
|
|
||||||
memcpy(type, type_line+5, typelen);
|
memcpy(type, type_line+5, typelen);
|
||||||
type[typelen] = 0;
|
type[typelen] = 0;
|
||||||
|
|
||||||
/* Verify that the object matches */
|
/* Verify that the object matches */
|
||||||
if (get_sha1_hex(object + 7, sha1))
|
if (get_sha1_hex(object + 7, sha1))
|
||||||
return -1;
|
return error("char%d: could not get SHA1 hash but this is really odd since i got it before !\n", 7);
|
||||||
|
|
||||||
if (verify_object(sha1, type))
|
if (verify_object(sha1, type))
|
||||||
return -1;
|
return error("char%d: could not verify object %s\n", 7, sha1);
|
||||||
|
|
||||||
/* Verify the tag-name: we don't allow control characters or spaces in it */
|
/* Verify the tag-name: we don't allow control characters or spaces in it */
|
||||||
tag_line += 4;
|
tag_line += 4;
|
||||||
@ -90,14 +94,14 @@ static int verify_tag(char *buffer, unsigned long size)
|
|||||||
break;
|
break;
|
||||||
if (c > ' ')
|
if (c > ' ')
|
||||||
continue;
|
continue;
|
||||||
return -1;
|
return error("char%td: could not verify tag name\n", tag_line - buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Verify the tagger line */
|
/* Verify the tagger line */
|
||||||
tagger_line = tag_line;
|
tagger_line = tag_line;
|
||||||
|
|
||||||
if (memcmp(tagger_line, "tagger", 6) || (tagger_line[6] == '\n'))
|
if (memcmp(tagger_line, "tagger", 6) || (tagger_line[6] == '\n'))
|
||||||
return -1;
|
return error("char%td: could not find \"tagger\"\n", tagger_line - buffer);
|
||||||
|
|
||||||
/* The actual stuff afterwards we don't care about.. */
|
/* The actual stuff afterwards we don't care about.. */
|
||||||
return 0;
|
return 0;
|
||||||
@ -105,8 +109,8 @@ static int verify_tag(char *buffer, unsigned long size)
|
|||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
unsigned long size;
|
unsigned long size = 4096;
|
||||||
char buffer[MAXSIZE];
|
char *buffer = malloc(size);
|
||||||
unsigned char result_sha1[20];
|
unsigned char result_sha1[20];
|
||||||
|
|
||||||
if (argc != 1)
|
if (argc != 1)
|
||||||
@ -114,13 +118,9 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
setup_git_directory();
|
setup_git_directory();
|
||||||
|
|
||||||
// Read the signature
|
if (read_pipe(0, &buffer, &size)) {
|
||||||
size = 0;
|
free(buffer);
|
||||||
for (;;) {
|
die("could not read from stdin");
|
||||||
int ret = xread(0, buffer + size, MAXSIZE - size);
|
|
||||||
if (ret <= 0)
|
|
||||||
break;
|
|
||||||
size += ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify it for some basic sanity: it needs to start with "object <sha1>\ntype\ntagger "
|
// Verify it for some basic sanity: it needs to start with "object <sha1>\ntype\ntagger "
|
||||||
@ -129,6 +129,9 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
if (write_sha1_file(buffer, size, tag_type, result_sha1) < 0)
|
if (write_sha1_file(buffer, size, tag_type, result_sha1) < 0)
|
||||||
die("unable to write tag file");
|
die("unable to write tag file");
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
printf("%s\n", sha1_to_hex(result_sha1));
|
printf("%s\n", sha1_to_hex(result_sha1));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
7
object.c
7
object.c
@ -9,7 +9,7 @@ struct object **objs;
|
|||||||
static int nr_objs;
|
static int nr_objs;
|
||||||
int obj_allocs;
|
int obj_allocs;
|
||||||
|
|
||||||
int track_object_refs = 1;
|
int track_object_refs = 0;
|
||||||
|
|
||||||
static int hashtable_index(const unsigned char *sha1)
|
static int hashtable_index(const unsigned char *sha1)
|
||||||
{
|
{
|
||||||
@ -200,8 +200,11 @@ struct object *parse_object(const unsigned char *sha1)
|
|||||||
obj = &blob->object;
|
obj = &blob->object;
|
||||||
} else if (!strcmp(type, tree_type)) {
|
} else if (!strcmp(type, tree_type)) {
|
||||||
struct tree *tree = lookup_tree(sha1);
|
struct tree *tree = lookup_tree(sha1);
|
||||||
parse_tree_buffer(tree, buffer, size);
|
|
||||||
obj = &tree->object;
|
obj = &tree->object;
|
||||||
|
if (!tree->object.parsed) {
|
||||||
|
parse_tree_buffer(tree, buffer, size);
|
||||||
|
buffer = NULL;
|
||||||
|
}
|
||||||
} else if (!strcmp(type, commit_type)) {
|
} else if (!strcmp(type, commit_type)) {
|
||||||
struct commit *commit = lookup_commit(sha1);
|
struct commit *commit = lookup_commit(sha1);
|
||||||
parse_commit_buffer(commit, buffer, size);
|
parse_commit_buffer(commit, buffer, size);
|
||||||
|
@ -108,7 +108,8 @@ static int get_value(const char* key_, const char* regex_)
|
|||||||
|
|
||||||
int main(int argc, const char **argv)
|
int main(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
setup_git_directory();
|
int nongit = 0;
|
||||||
|
setup_git_directory_gently(&nongit);
|
||||||
|
|
||||||
while (1 < argc) {
|
while (1 < argc) {
|
||||||
if (!strcmp(argv[1], "--int"))
|
if (!strcmp(argv[1], "--int"))
|
||||||
|
15
revision.c
15
revision.c
@ -63,14 +63,13 @@ void mark_tree_uninteresting(struct tree *tree)
|
|||||||
return;
|
return;
|
||||||
if (parse_tree(tree) < 0)
|
if (parse_tree(tree) < 0)
|
||||||
die("bad tree %s", sha1_to_hex(obj->sha1));
|
die("bad tree %s", sha1_to_hex(obj->sha1));
|
||||||
entry = tree->entries;
|
entry = create_tree_entry_list(tree);
|
||||||
tree->entries = NULL;
|
|
||||||
while (entry) {
|
while (entry) {
|
||||||
struct tree_entry_list *next = entry->next;
|
struct tree_entry_list *next = entry->next;
|
||||||
if (entry->directory)
|
if (entry->directory)
|
||||||
mark_tree_uninteresting(entry->item.tree);
|
mark_tree_uninteresting(lookup_tree(entry->sha1));
|
||||||
else
|
else
|
||||||
mark_blob_uninteresting(entry->item.blob);
|
mark_blob_uninteresting(lookup_blob(entry->sha1));
|
||||||
free(entry);
|
free(entry);
|
||||||
entry = next;
|
entry = next;
|
||||||
}
|
}
|
||||||
@ -733,6 +732,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
|||||||
revs->abbrev = DEFAULT_ABBREV;
|
revs->abbrev = DEFAULT_ABBREV;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strncmp(arg, "--abbrev=", 9)) {
|
||||||
|
revs->abbrev = strtoul(arg + 9, NULL, 10);
|
||||||
|
if (revs->abbrev < MINIMUM_ABBREV)
|
||||||
|
revs->abbrev = MINIMUM_ABBREV;
|
||||||
|
else if (revs->abbrev > 40)
|
||||||
|
revs->abbrev = 40;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!strcmp(arg, "--abbrev-commit")) {
|
if (!strcmp(arg, "--abbrev-commit")) {
|
||||||
revs->abbrev_commit = 1;
|
revs->abbrev_commit = 1;
|
||||||
continue;
|
continue;
|
||||||
|
@ -58,6 +58,8 @@ struct rev_info {
|
|||||||
unsigned int abbrev;
|
unsigned int abbrev;
|
||||||
enum cmit_fmt commit_format;
|
enum cmit_fmt commit_format;
|
||||||
struct log_info *loginfo;
|
struct log_info *loginfo;
|
||||||
|
int nr, total;
|
||||||
|
const char *mime_boundary;
|
||||||
|
|
||||||
/* special limits */
|
/* special limits */
|
||||||
int max_count;
|
int max_count;
|
||||||
|
129
sha1_file.c
129
sha1_file.c
@ -1399,6 +1399,25 @@ int move_temp_to_file(const char *tmpfile, char *filename)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int write_buffer(int fd, const void *buf, size_t len)
|
||||||
|
{
|
||||||
|
while (len) {
|
||||||
|
ssize_t size;
|
||||||
|
|
||||||
|
size = write(fd, buf, len);
|
||||||
|
if (!size)
|
||||||
|
return error("file write: disk full");
|
||||||
|
if (size < 0) {
|
||||||
|
if (errno == EINTR || errno == EAGAIN)
|
||||||
|
continue;
|
||||||
|
return error("file write error (%s)", strerror(errno));
|
||||||
|
}
|
||||||
|
len -= size;
|
||||||
|
buf += size;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
|
int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
|
||||||
{
|
{
|
||||||
int size;
|
int size;
|
||||||
@ -1465,8 +1484,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
|
|||||||
deflateEnd(&stream);
|
deflateEnd(&stream);
|
||||||
size = stream.total_out;
|
size = stream.total_out;
|
||||||
|
|
||||||
if (write(fd, compressed, size) != size)
|
if (write_buffer(fd, compressed, size) < 0)
|
||||||
die("unable to write file");
|
die("unable to write sha1 file");
|
||||||
fchmod(fd, 0444);
|
fchmod(fd, 0444);
|
||||||
close(fd);
|
close(fd);
|
||||||
free(compressed);
|
free(compressed);
|
||||||
@ -1474,22 +1493,21 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
|
|||||||
return move_temp_to_file(tmpfile, filename);
|
return move_temp_to_file(tmpfile, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
int write_sha1_to_fd(int fd, const unsigned char *sha1)
|
/*
|
||||||
|
* We need to unpack and recompress the object for writing
|
||||||
|
* it out to a different file.
|
||||||
|
*/
|
||||||
|
static void *repack_object(const unsigned char *sha1, unsigned long *objsize)
|
||||||
{
|
{
|
||||||
ssize_t size;
|
size_t size;
|
||||||
unsigned long objsize;
|
|
||||||
int posn = 0;
|
|
||||||
void *map = map_sha1_file_internal(sha1, &objsize);
|
|
||||||
void *buf = map;
|
|
||||||
void *temp_obj = NULL;
|
|
||||||
z_stream stream;
|
z_stream stream;
|
||||||
|
|
||||||
if (!buf) {
|
|
||||||
unsigned char *unpacked;
|
unsigned char *unpacked;
|
||||||
unsigned long len;
|
unsigned long len;
|
||||||
char type[20];
|
char type[20];
|
||||||
char hdr[50];
|
char hdr[50];
|
||||||
int hdrlen;
|
int hdrlen;
|
||||||
|
void *buf;
|
||||||
|
|
||||||
// need to unpack and recompress it by itself
|
// need to unpack and recompress it by itself
|
||||||
unpacked = read_packed_sha1(sha1, type, &len);
|
unpacked = read_packed_sha1(sha1, type, &len);
|
||||||
|
|
||||||
@ -1499,7 +1517,7 @@ int write_sha1_to_fd(int fd, const unsigned char *sha1)
|
|||||||
memset(&stream, 0, sizeof(stream));
|
memset(&stream, 0, sizeof(stream));
|
||||||
deflateInit(&stream, Z_BEST_COMPRESSION);
|
deflateInit(&stream, Z_BEST_COMPRESSION);
|
||||||
size = deflateBound(&stream, len + hdrlen);
|
size = deflateBound(&stream, len + hdrlen);
|
||||||
temp_obj = buf = xmalloc(size);
|
buf = xmalloc(size);
|
||||||
|
|
||||||
/* Compress it */
|
/* Compress it */
|
||||||
stream.next_out = buf;
|
stream.next_out = buf;
|
||||||
@ -1519,28 +1537,26 @@ int write_sha1_to_fd(int fd, const unsigned char *sha1)
|
|||||||
deflateEnd(&stream);
|
deflateEnd(&stream);
|
||||||
free(unpacked);
|
free(unpacked);
|
||||||
|
|
||||||
objsize = stream.total_out;
|
*objsize = stream.total_out;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
int write_sha1_to_fd(int fd, const unsigned char *sha1)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
unsigned long objsize;
|
||||||
|
void *buf = map_sha1_file_internal(sha1, &objsize);
|
||||||
|
|
||||||
|
if (buf) {
|
||||||
|
retval = write_buffer(fd, buf, objsize);
|
||||||
|
munmap(buf, objsize);
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
buf = repack_object(sha1, &objsize);
|
||||||
size = write(fd, buf + posn, objsize - posn);
|
retval = write_buffer(fd, buf, objsize);
|
||||||
if (size <= 0) {
|
free(buf);
|
||||||
if (!size) {
|
return retval;
|
||||||
fprintf(stderr, "write closed\n");
|
|
||||||
} else {
|
|
||||||
perror("write ");
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
posn += size;
|
|
||||||
} while (posn < objsize);
|
|
||||||
|
|
||||||
if (map)
|
|
||||||
munmap(map, objsize);
|
|
||||||
if (temp_obj)
|
|
||||||
free(temp_obj);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
|
int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
|
||||||
@ -1579,7 +1595,8 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
|
|||||||
SHA1_Update(&c, discard, sizeof(discard) -
|
SHA1_Update(&c, discard, sizeof(discard) -
|
||||||
stream.avail_out);
|
stream.avail_out);
|
||||||
} while (stream.avail_in && ret == Z_OK);
|
} while (stream.avail_in && ret == Z_OK);
|
||||||
write(local, buffer, *bufposn - stream.avail_in);
|
if (write_buffer(local, buffer, *bufposn - stream.avail_in) < 0)
|
||||||
|
die("unable to write sha1 file");
|
||||||
memmove(buffer, buffer + *bufposn - stream.avail_in,
|
memmove(buffer, buffer + *bufposn - stream.avail_in,
|
||||||
stream.avail_in);
|
stream.avail_in);
|
||||||
*bufposn = stream.avail_in;
|
*bufposn = stream.avail_in;
|
||||||
@ -1645,16 +1662,24 @@ int has_sha1_file(const unsigned char *sha1)
|
|||||||
return find_sha1_file(sha1, &st) ? 1 : 0;
|
return find_sha1_file(sha1, &st) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
|
/*
|
||||||
|
* reads from fd as long as possible into a supplied buffer of size bytes.
|
||||||
|
* If neccessary the buffer's size is increased using realloc()
|
||||||
|
*
|
||||||
|
* returns 0 if anything went fine and -1 otherwise
|
||||||
|
*
|
||||||
|
* NOTE: both buf and size may change, but even when -1 is returned
|
||||||
|
* you still have to free() it yourself.
|
||||||
|
*/
|
||||||
|
int read_pipe(int fd, char** return_buf, unsigned long* return_size)
|
||||||
{
|
{
|
||||||
unsigned long size = 4096;
|
char* buf = *return_buf;
|
||||||
char *buf = malloc(size);
|
unsigned long size = *return_size;
|
||||||
int iret, ret;
|
int iret;
|
||||||
unsigned long off = 0;
|
unsigned long off = 0;
|
||||||
unsigned char hdr[50];
|
|
||||||
int hdrlen;
|
|
||||||
do {
|
do {
|
||||||
iret = read(fd, buf + off, size - off);
|
iret = xread(fd, buf + off, size - off);
|
||||||
if (iret > 0) {
|
if (iret > 0) {
|
||||||
off += iret;
|
off += iret;
|
||||||
if (off == size) {
|
if (off == size) {
|
||||||
@ -1663,16 +1688,34 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (iret > 0);
|
} while (iret > 0);
|
||||||
if (iret < 0) {
|
|
||||||
|
*return_buf = buf;
|
||||||
|
*return_size = off;
|
||||||
|
|
||||||
|
if (iret < 0)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
|
||||||
|
{
|
||||||
|
unsigned long size = 4096;
|
||||||
|
char *buf = malloc(size);
|
||||||
|
int ret;
|
||||||
|
unsigned char hdr[50];
|
||||||
|
int hdrlen;
|
||||||
|
|
||||||
|
if (read_pipe(fd, &buf, &size)) {
|
||||||
free(buf);
|
free(buf);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!type)
|
if (!type)
|
||||||
type = blob_type;
|
type = blob_type;
|
||||||
if (write_object)
|
if (write_object)
|
||||||
ret = write_sha1_file(buf, off, type, sha1);
|
ret = write_sha1_file(buf, size, type, sha1);
|
||||||
else {
|
else {
|
||||||
write_sha1_file_prepare(buf, off, type, sha1, hdr, &hdrlen);
|
write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
free(buf);
|
free(buf);
|
||||||
|
@ -75,7 +75,7 @@ test_expect_success \
|
|||||||
git-update-index --add yomin &&
|
git-update-index --add yomin &&
|
||||||
git-read-tree -m -u $treeH $treeM &&
|
git-read-tree -m -u $treeH $treeM &&
|
||||||
git-ls-files --stage >4.out || return 1
|
git-ls-files --stage >4.out || return 1
|
||||||
diff --unified=0 M.out 4.out >4diff.out
|
diff -U0 M.out 4.out >4diff.out
|
||||||
compare_change 4diff.out expected &&
|
compare_change 4diff.out expected &&
|
||||||
check_cache_at yomin clean &&
|
check_cache_at yomin clean &&
|
||||||
sum bozbar frotz nitfol >actual4.sum &&
|
sum bozbar frotz nitfol >actual4.sum &&
|
||||||
@ -94,7 +94,7 @@ test_expect_success \
|
|||||||
echo yomin yomin >yomin &&
|
echo yomin yomin >yomin &&
|
||||||
git-read-tree -m -u $treeH $treeM &&
|
git-read-tree -m -u $treeH $treeM &&
|
||||||
git-ls-files --stage >5.out || return 1
|
git-ls-files --stage >5.out || return 1
|
||||||
diff --unified=0 M.out 5.out >5diff.out
|
diff -U0 M.out 5.out >5diff.out
|
||||||
compare_change 5diff.out expected &&
|
compare_change 5diff.out expected &&
|
||||||
check_cache_at yomin dirty &&
|
check_cache_at yomin dirty &&
|
||||||
sum bozbar frotz nitfol >actual5.sum &&
|
sum bozbar frotz nitfol >actual5.sum &&
|
||||||
@ -112,7 +112,7 @@ test_expect_success \
|
|||||||
git-update-index --add frotz &&
|
git-update-index --add frotz &&
|
||||||
git-read-tree -m -u $treeH $treeM &&
|
git-read-tree -m -u $treeH $treeM &&
|
||||||
git-ls-files --stage >6.out &&
|
git-ls-files --stage >6.out &&
|
||||||
diff --unified=0 M.out 6.out &&
|
diff -U0 M.out 6.out &&
|
||||||
check_cache_at frotz clean &&
|
check_cache_at frotz clean &&
|
||||||
sum bozbar frotz nitfol >actual3.sum &&
|
sum bozbar frotz nitfol >actual3.sum &&
|
||||||
cmp M.sum actual3.sum &&
|
cmp M.sum actual3.sum &&
|
||||||
@ -129,7 +129,7 @@ test_expect_success \
|
|||||||
echo frotz frotz >frotz &&
|
echo frotz frotz >frotz &&
|
||||||
git-read-tree -m -u $treeH $treeM &&
|
git-read-tree -m -u $treeH $treeM &&
|
||||||
git-ls-files --stage >7.out &&
|
git-ls-files --stage >7.out &&
|
||||||
diff --unified=0 M.out 7.out &&
|
diff -U0 M.out 7.out &&
|
||||||
check_cache_at frotz dirty &&
|
check_cache_at frotz dirty &&
|
||||||
sum bozbar frotz nitfol >actual7.sum &&
|
sum bozbar frotz nitfol >actual7.sum &&
|
||||||
if cmp M.sum actual7.sum; then false; else :; fi &&
|
if cmp M.sum actual7.sum; then false; else :; fi &&
|
||||||
@ -206,7 +206,7 @@ test_expect_success \
|
|||||||
git-update-index --add nitfol &&
|
git-update-index --add nitfol &&
|
||||||
git-read-tree -m -u $treeH $treeM &&
|
git-read-tree -m -u $treeH $treeM &&
|
||||||
git-ls-files --stage >14.out || return 1
|
git-ls-files --stage >14.out || return 1
|
||||||
diff --unified=0 M.out 14.out >14diff.out
|
diff -U0 M.out 14.out >14diff.out
|
||||||
compare_change 14diff.out expected &&
|
compare_change 14diff.out expected &&
|
||||||
sum bozbar frotz >actual14.sum &&
|
sum bozbar frotz >actual14.sum &&
|
||||||
grep -v nitfol M.sum > expected14.sum &&
|
grep -v nitfol M.sum > expected14.sum &&
|
||||||
@ -227,7 +227,7 @@ test_expect_success \
|
|||||||
echo nitfol nitfol nitfol >nitfol &&
|
echo nitfol nitfol nitfol >nitfol &&
|
||||||
git-read-tree -m -u $treeH $treeM &&
|
git-read-tree -m -u $treeH $treeM &&
|
||||||
git-ls-files --stage >15.out || return 1
|
git-ls-files --stage >15.out || return 1
|
||||||
diff --unified=0 M.out 15.out >15diff.out
|
diff -U0 M.out 15.out >15diff.out
|
||||||
compare_change 15diff.out expected &&
|
compare_change 15diff.out expected &&
|
||||||
check_cache_at nitfol dirty &&
|
check_cache_at nitfol dirty &&
|
||||||
sum bozbar frotz >actual15.sum &&
|
sum bozbar frotz >actual15.sum &&
|
||||||
@ -264,7 +264,7 @@ test_expect_success \
|
|||||||
git-update-index --add bozbar &&
|
git-update-index --add bozbar &&
|
||||||
git-read-tree -m -u $treeH $treeM &&
|
git-read-tree -m -u $treeH $treeM &&
|
||||||
git-ls-files --stage >18.out &&
|
git-ls-files --stage >18.out &&
|
||||||
diff --unified=0 M.out 18.out &&
|
diff -U0 M.out 18.out &&
|
||||||
check_cache_at bozbar clean &&
|
check_cache_at bozbar clean &&
|
||||||
sum bozbar frotz nitfol >actual18.sum &&
|
sum bozbar frotz nitfol >actual18.sum &&
|
||||||
cmp M.sum actual18.sum'
|
cmp M.sum actual18.sum'
|
||||||
@ -278,7 +278,7 @@ test_expect_success \
|
|||||||
echo gnusto gnusto >bozbar &&
|
echo gnusto gnusto >bozbar &&
|
||||||
git-read-tree -m -u $treeH $treeM &&
|
git-read-tree -m -u $treeH $treeM &&
|
||||||
git-ls-files --stage >19.out &&
|
git-ls-files --stage >19.out &&
|
||||||
diff --unified=0 M.out 19.out &&
|
diff -U0 M.out 19.out &&
|
||||||
check_cache_at bozbar dirty &&
|
check_cache_at bozbar dirty &&
|
||||||
sum frotz nitfol >actual19.sum &&
|
sum frotz nitfol >actual19.sum &&
|
||||||
grep -v bozbar M.sum > expected19.sum &&
|
grep -v bozbar M.sum > expected19.sum &&
|
||||||
@ -297,7 +297,7 @@ test_expect_success \
|
|||||||
git-update-index --add bozbar &&
|
git-update-index --add bozbar &&
|
||||||
git-read-tree -m -u $treeH $treeM &&
|
git-read-tree -m -u $treeH $treeM &&
|
||||||
git-ls-files --stage >20.out &&
|
git-ls-files --stage >20.out &&
|
||||||
diff --unified=0 M.out 20.out &&
|
diff -U0 M.out 20.out &&
|
||||||
check_cache_at bozbar clean &&
|
check_cache_at bozbar clean &&
|
||||||
sum bozbar frotz nitfol >actual20.sum &&
|
sum bozbar frotz nitfol >actual20.sum &&
|
||||||
cmp M.sum actual20.sum'
|
cmp M.sum actual20.sum'
|
||||||
@ -338,7 +338,7 @@ test_expect_success \
|
|||||||
git-update-index --add DF &&
|
git-update-index --add DF &&
|
||||||
git-read-tree -m -u $treeDF $treeDFDF &&
|
git-read-tree -m -u $treeDF $treeDFDF &&
|
||||||
git-ls-files --stage >DFDFcheck.out &&
|
git-ls-files --stage >DFDFcheck.out &&
|
||||||
diff --unified=0 DFDF.out DFDFcheck.out &&
|
diff -U0 DFDF.out DFDFcheck.out &&
|
||||||
check_cache_at DF/DF clean'
|
check_cache_at DF/DF clean'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
21
tree-walk.c
21
tree-walk.c
@ -47,18 +47,33 @@ void update_tree_entry(struct tree_desc *desc)
|
|||||||
desc->size = size - len;
|
desc->size = size - len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *get_mode(const char *str, unsigned int *modep)
|
||||||
|
{
|
||||||
|
unsigned char c;
|
||||||
|
unsigned int mode = 0;
|
||||||
|
|
||||||
|
while ((c = *str++) != ' ') {
|
||||||
|
if (c < '0' || c > '7')
|
||||||
|
return NULL;
|
||||||
|
mode = (mode << 3) + (c - '0');
|
||||||
|
}
|
||||||
|
*modep = mode;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
|
const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
|
||||||
{
|
{
|
||||||
void *tree = desc->buf;
|
void *tree = desc->buf;
|
||||||
unsigned long size = desc->size;
|
unsigned long size = desc->size;
|
||||||
int len = strlen(tree)+1;
|
int len = strlen(tree)+1;
|
||||||
const unsigned char *sha1 = tree + len;
|
const unsigned char *sha1 = tree + len;
|
||||||
const char *path = strchr(tree, ' ');
|
const char *path;
|
||||||
unsigned int mode;
|
unsigned int mode;
|
||||||
|
|
||||||
if (!path || size < len + 20 || sscanf(tree, "%o", &mode) != 1)
|
path = get_mode(tree, &mode);
|
||||||
|
if (!path || size < len + 20)
|
||||||
die("corrupt tree file");
|
die("corrupt tree file");
|
||||||
*pathp = path+1;
|
*pathp = path;
|
||||||
*modep = canon_mode(mode);
|
*modep = canon_mode(mode);
|
||||||
return sha1;
|
return sha1;
|
||||||
}
|
}
|
||||||
|
152
tree.c
152
tree.c
@ -3,11 +3,12 @@
|
|||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
#include "tag.h"
|
#include "tag.h"
|
||||||
|
#include "tree-walk.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
const char *tree_type = "tree";
|
const char *tree_type = "tree";
|
||||||
|
|
||||||
static int read_one_entry(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
|
static int read_one_entry(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
|
||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
unsigned int size;
|
unsigned int size;
|
||||||
@ -77,19 +78,26 @@ int read_tree_recursive(struct tree *tree,
|
|||||||
int stage, const char **match,
|
int stage, const char **match,
|
||||||
read_tree_fn_t fn)
|
read_tree_fn_t fn)
|
||||||
{
|
{
|
||||||
struct tree_entry_list *list;
|
struct tree_desc desc;
|
||||||
|
|
||||||
if (parse_tree(tree))
|
if (parse_tree(tree))
|
||||||
return -1;
|
return -1;
|
||||||
list = tree->entries;
|
|
||||||
while (list) {
|
desc.buf = tree->buffer;
|
||||||
struct tree_entry_list *current = list;
|
desc.size = tree->size;
|
||||||
list = list->next;
|
|
||||||
if (!match_tree_entry(base, baselen, current->name,
|
while (desc.size) {
|
||||||
current->mode, match))
|
unsigned mode;
|
||||||
|
const char *name;
|
||||||
|
const unsigned char *sha1;
|
||||||
|
|
||||||
|
sha1 = tree_entry_extract(&desc, &name, &mode);
|
||||||
|
update_tree_entry(&desc);
|
||||||
|
|
||||||
|
if (!match_tree_entry(base, baselen, name, mode, match))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
switch (fn(current->item.any->sha1, base, baselen,
|
switch (fn(sha1, base, baselen, name, mode, stage)) {
|
||||||
current->name, current->mode, stage)) {
|
|
||||||
case 0:
|
case 0:
|
||||||
continue;
|
continue;
|
||||||
case READ_TREE_RECURSIVE:
|
case READ_TREE_RECURSIVE:
|
||||||
@ -97,16 +105,16 @@ int read_tree_recursive(struct tree *tree,
|
|||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (current->directory) {
|
if (S_ISDIR(mode)) {
|
||||||
int retval;
|
int retval;
|
||||||
int pathlen = strlen(current->name);
|
int pathlen = strlen(name);
|
||||||
char *newbase;
|
char *newbase;
|
||||||
|
|
||||||
newbase = xmalloc(baselen + 1 + pathlen);
|
newbase = xmalloc(baselen + 1 + pathlen);
|
||||||
memcpy(newbase, base, baselen);
|
memcpy(newbase, base, baselen);
|
||||||
memcpy(newbase + baselen, current->name, pathlen);
|
memcpy(newbase + baselen, name, pathlen);
|
||||||
newbase[baselen + pathlen] = '/';
|
newbase[baselen + pathlen] = '/';
|
||||||
retval = read_tree_recursive(current->item.tree,
|
retval = read_tree_recursive(lookup_tree(sha1),
|
||||||
newbase,
|
newbase,
|
||||||
baselen + pathlen + 1,
|
baselen + pathlen + 1,
|
||||||
stage, match, fn);
|
stage, match, fn);
|
||||||
@ -143,62 +151,97 @@ struct tree *lookup_tree(const unsigned char *sha1)
|
|||||||
return (struct tree *) obj;
|
return (struct tree *) obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int track_tree_refs(struct tree *item)
|
||||||
|
{
|
||||||
|
int n_refs = 0, i;
|
||||||
|
struct object_refs *refs;
|
||||||
|
struct tree_desc desc;
|
||||||
|
|
||||||
|
/* Count how many entries there are.. */
|
||||||
|
desc.buf = item->buffer;
|
||||||
|
desc.size = item->size;
|
||||||
|
while (desc.size) {
|
||||||
|
n_refs++;
|
||||||
|
update_tree_entry(&desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate object refs and walk it again.. */
|
||||||
|
i = 0;
|
||||||
|
refs = alloc_object_refs(n_refs);
|
||||||
|
desc.buf = item->buffer;
|
||||||
|
desc.size = item->size;
|
||||||
|
while (desc.size) {
|
||||||
|
unsigned mode;
|
||||||
|
const char *name;
|
||||||
|
const unsigned char *sha1;
|
||||||
|
struct object *obj;
|
||||||
|
|
||||||
|
sha1 = tree_entry_extract(&desc, &name, &mode);
|
||||||
|
update_tree_entry(&desc);
|
||||||
|
if (S_ISDIR(mode))
|
||||||
|
obj = &lookup_tree(sha1)->object;
|
||||||
|
else
|
||||||
|
obj = &lookup_blob(sha1)->object;
|
||||||
|
refs->ref[i++] = obj;
|
||||||
|
}
|
||||||
|
set_object_refs(&item->object, refs);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)
|
int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)
|
||||||
{
|
{
|
||||||
void *bufptr = buffer;
|
|
||||||
struct tree_entry_list **list_p;
|
|
||||||
int n_refs = 0;
|
|
||||||
|
|
||||||
if (item->object.parsed)
|
if (item->object.parsed)
|
||||||
return 0;
|
return 0;
|
||||||
item->object.parsed = 1;
|
item->object.parsed = 1;
|
||||||
list_p = &item->entries;
|
item->buffer = buffer;
|
||||||
while (size) {
|
item->size = size;
|
||||||
struct object *obj;
|
|
||||||
|
if (track_object_refs)
|
||||||
|
track_tree_refs(item);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tree_entry_list *create_tree_entry_list(struct tree *tree)
|
||||||
|
{
|
||||||
|
struct tree_desc desc;
|
||||||
|
struct tree_entry_list *ret = NULL;
|
||||||
|
struct tree_entry_list **list_p = &ret;
|
||||||
|
|
||||||
|
desc.buf = tree->buffer;
|
||||||
|
desc.size = tree->size;
|
||||||
|
|
||||||
|
while (desc.size) {
|
||||||
|
unsigned mode;
|
||||||
|
const char *path;
|
||||||
|
const unsigned char *sha1;
|
||||||
struct tree_entry_list *entry;
|
struct tree_entry_list *entry;
|
||||||
int len = 1+strlen(bufptr);
|
|
||||||
unsigned char *file_sha1 = bufptr + len;
|
sha1 = tree_entry_extract(&desc, &path, &mode);
|
||||||
char *path = strchr(bufptr, ' ');
|
|
||||||
unsigned int mode;
|
|
||||||
if (size < len + 20 || !path ||
|
|
||||||
sscanf(bufptr, "%o", &mode) != 1)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
entry = xmalloc(sizeof(struct tree_entry_list));
|
entry = xmalloc(sizeof(struct tree_entry_list));
|
||||||
entry->name = strdup(path + 1);
|
entry->name = path;
|
||||||
|
entry->sha1 = sha1;
|
||||||
|
entry->mode = mode;
|
||||||
entry->directory = S_ISDIR(mode) != 0;
|
entry->directory = S_ISDIR(mode) != 0;
|
||||||
entry->executable = (mode & S_IXUSR) != 0;
|
entry->executable = (mode & S_IXUSR) != 0;
|
||||||
entry->symlink = S_ISLNK(mode) != 0;
|
entry->symlink = S_ISLNK(mode) != 0;
|
||||||
entry->zeropad = *(char *)bufptr == '0';
|
entry->zeropad = *(const char *)(desc.buf) == '0';
|
||||||
entry->mode = mode;
|
|
||||||
entry->next = NULL;
|
entry->next = NULL;
|
||||||
|
|
||||||
bufptr += len + 20;
|
update_tree_entry(&desc);
|
||||||
size -= len + 20;
|
|
||||||
|
|
||||||
if (entry->directory) {
|
|
||||||
entry->item.tree = lookup_tree(file_sha1);
|
|
||||||
obj = &entry->item.tree->object;
|
|
||||||
} else {
|
|
||||||
entry->item.blob = lookup_blob(file_sha1);
|
|
||||||
obj = &entry->item.blob->object;
|
|
||||||
}
|
|
||||||
if (obj)
|
|
||||||
n_refs++;
|
|
||||||
*list_p = entry;
|
*list_p = entry;
|
||||||
list_p = &entry->next;
|
list_p = &entry->next;
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (track_object_refs) {
|
void free_tree_entry_list(struct tree_entry_list *list)
|
||||||
struct tree_entry_list *entry;
|
{
|
||||||
unsigned i = 0;
|
while (list) {
|
||||||
struct object_refs *refs = alloc_object_refs(n_refs);
|
struct tree_entry_list *next = list->next;
|
||||||
for (entry = item->entries; entry; entry = entry->next)
|
free(list);
|
||||||
refs->ref[i++] = entry->item.any;
|
list = next;
|
||||||
set_object_refs(&item->object, refs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int parse_tree(struct tree *item)
|
int parse_tree(struct tree *item)
|
||||||
@ -206,7 +249,6 @@ int parse_tree(struct tree *item)
|
|||||||
char type[20];
|
char type[20];
|
||||||
void *buffer;
|
void *buffer;
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (item->object.parsed)
|
if (item->object.parsed)
|
||||||
return 0;
|
return 0;
|
||||||
@ -219,9 +261,7 @@ int parse_tree(struct tree *item)
|
|||||||
return error("Object %s not a tree",
|
return error("Object %s not a tree",
|
||||||
sha1_to_hex(item->object.sha1));
|
sha1_to_hex(item->object.sha1));
|
||||||
}
|
}
|
||||||
ret = parse_tree_buffer(item, buffer, size);
|
return parse_tree_buffer(item, buffer, size);
|
||||||
free(buffer);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct tree *parse_tree_indirect(const unsigned char *sha1)
|
struct tree *parse_tree_indirect(const unsigned char *sha1)
|
||||||
|
16
tree.h
16
tree.h
@ -12,19 +12,19 @@ struct tree_entry_list {
|
|||||||
unsigned symlink : 1;
|
unsigned symlink : 1;
|
||||||
unsigned zeropad : 1;
|
unsigned zeropad : 1;
|
||||||
unsigned int mode;
|
unsigned int mode;
|
||||||
char *name;
|
const char *name;
|
||||||
union {
|
const unsigned char *sha1;
|
||||||
struct object *any;
|
|
||||||
struct tree *tree;
|
|
||||||
struct blob *blob;
|
|
||||||
} item;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tree {
|
struct tree {
|
||||||
struct object object;
|
struct object object;
|
||||||
struct tree_entry_list *entries;
|
void *buffer;
|
||||||
|
unsigned long size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct tree_entry_list *create_tree_entry_list(struct tree *);
|
||||||
|
void free_tree_entry_list(struct tree_entry_list *);
|
||||||
|
|
||||||
struct tree *lookup_tree(const unsigned char *sha1);
|
struct tree *lookup_tree(const unsigned char *sha1);
|
||||||
|
|
||||||
int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size);
|
int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size);
|
||||||
@ -35,7 +35,7 @@ int parse_tree(struct tree *tree);
|
|||||||
struct tree *parse_tree_indirect(const unsigned char *sha1);
|
struct tree *parse_tree_indirect(const unsigned char *sha1);
|
||||||
|
|
||||||
#define READ_TREE_RECURSIVE 1
|
#define READ_TREE_RECURSIVE 1
|
||||||
typedef int (*read_tree_fn_t)(unsigned char *, const char *, int, const char *, unsigned int, int);
|
typedef int (*read_tree_fn_t)(const unsigned char *, const char *, int, const char *, unsigned int, int);
|
||||||
|
|
||||||
extern int read_tree_recursive(struct tree *tree,
|
extern int read_tree_recursive(struct tree *tree,
|
||||||
const char *base, int baselen,
|
const char *base, int baselen,
|
||||||
|
Loading…
Reference in New Issue
Block a user