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:
Junio C Hamano 2006-05-28 22:47:53 -07:00
commit 0a2586c807
73 changed files with 2541 additions and 1042 deletions

2
.gitignore vendored
View File

@ -77,6 +77,7 @@ git-prune
git-prune-packed
git-pull
git-push
git-quiltimport
git-read-tree
git-rebase
git-receive-pack
@ -115,6 +116,7 @@ git-update-index
git-update-ref
git-update-server-info
git-upload-pack
git-upload-tar
git-var
git-verify-pack
git-verify-tag

View File

@ -7,6 +7,7 @@ MAN7_TXT=git.txt
DOC_HTML=$(patsubst %.txt,%.html,$(MAN1_TXT) $(MAN7_TXT))
ARTICLES = tutorial
ARTICLES += tutorial-2
ARTICLES += core-tutorial
ARTICLES += cvs-migration
ARTICLES += diffcore
@ -51,9 +52,9 @@ man1: $(DOC_MAN1)
man7: $(DOC_MAN7)
install: man
$(INSTALL) -d -m755 $(DESTDIR)/$(man1) $(DESTDIR)/$(man7)
$(INSTALL) $(DOC_MAN1) $(DESTDIR)/$(man1)
$(INSTALL) $(DOC_MAN7) $(DESTDIR)/$(man7)
$(INSTALL) -d -m755 $(DESTDIR)$(man1) $(DESTDIR)$(man7)
$(INSTALL) $(DOC_MAN1) $(DESTDIR)$(man1)
$(INSTALL) $(DOC_MAN7) $(DESTDIR)$(man7)
#

View File

@ -14,11 +14,13 @@ DESCRIPTION
A simple wrapper for git-update-index to add files to the index,
for people used to do "cvs add".
It only adds non-ignored files, to add ignored files use
"git update-index --add".
OPTIONS
-------
<file>...::
Files to add to the index.
Files to add to the index (see gitlink:git-ls-files[1]).
-n::
Don't actually add the file(s), just show if they exist.
@ -68,6 +70,7 @@ git-add git-*.sh::
See Also
--------
gitlink:git-rm[1]
gitlink:git-ls-files[1]
Author
------

View File

@ -43,6 +43,9 @@ OPTIONS
<branchname>::
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>::
The new branch will be created with a HEAD equal to this. It may

View File

@ -8,12 +8,12 @@ git-cat-file - Provide content or type information for repository objects
SYNOPSIS
--------
'git-cat-file' [-t | -s | -e | <type>] <object>
'git-cat-file' [-t | -s | -e | -p | <type>] <object>
DESCRIPTION
-----------
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.
OPTIONS
@ -33,6 +33,9 @@ OPTIONS
Suppress all output; instead exit with zero status if <object>
exists and is a valid object.
-p::
Pretty-print the contents of <object> based on its type.
<type>::
Typically this matches the real type of <object> but asking
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 '-p' is specified, the contents of <object> are pretty-printed.
Otherwise the raw (though uncompressed) contents of the <object> will
be returned.

View File

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

View File

@ -35,7 +35,10 @@ OPTIONS
Force a re-read of everything.
-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::
If you have local modifications to one or more files that

View File

@ -9,8 +9,8 @@ git-clone - Clones a repository
SYNOPSIS
--------
[verse]
'git-clone' [-l [-s]] [-q] [-n] [--bare] [-o <name>] [-u <upload-pack>]
[--reference <repository>]
'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
[-o <name>] [-u <upload-pack>] [--reference <repository>]
<repository> [<directory>]
DESCRIPTION
@ -89,6 +89,11 @@ OPTIONS
the command to specify non-default path for the command
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>::
The (possibly remote) repository to clone from. It can
be any URL git-fetch supports.

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

View File

@ -80,13 +80,13 @@ file; just remove it, then commit.
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
------------------------------------------------
$ git whatchanged -p
$ git log -p
------------------------------------------------
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
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
meantime, then Bob's changes will be merged in, and she will need to
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
-------------------------------------
$ 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
@ -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
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
most recent commit in the currently checked-out branch can always be
referred to as HEAD, and the "parent" of any commit can always be
referred to by appending a caret, "^", to the end of the name of the
commit. So, for example,
Git history is represented as a series of interrelated commits. We
have already seen that the git log command can list those commits.
Note that first line of each git log entry also gives a name for the
commit:
-------------------------------------
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
the tree and the previous state, and
We can give this name to git show to see the details about this
commit.
-------------------------------------
git diff HEAD^^ HEAD^
$ git show c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
-------------------------------------
shows the difference between that previous state and the state two
commits ago. Also, HEAD~5 can be used as a shorthand for HEAD{caret}{caret}{caret}{caret}{caret},
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
But there other ways to refer to commits. You can use any initial
part of the name that is long enough to uniquely identify the commit:
-------------------------------------
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
the current branch and the most-recently committed tree in the
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
Every commit has at least one "parent" commit, which points to the
previous state of the project:
-------------------------------------
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
experimental.
You can also give commits convenient names of your own: after running
You can also give commits 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
this name with other people (for example, to identify a release
you can refer to 1b2e1d63ff by the name "v2.5". If you intend to
share this name with other people (for example, to identify a release
version), you should create a "tag" object, and perhaps sign it; see
gitlink:git-tag[1] for details.
You can revisit the old state of a tree, and make further
modifications if you wish, using git branch: the command
Any git command that needs to know a commit can take any of these
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
commit which you tagged with the name v2.5.
You can reset the state of any branch to an earlier commit at any
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
Be careful with that last command: in addition to losing any changes
in the working directory, it will also remove all later commits from
this branch. If this branch is the only branch containing those
commits, they will be lost. (Also, don't use "git reset" on a
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
----------
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
we've seen in the few examples above.
* The object database is the rather elegant system used to
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
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
case of complex non-linear history with lots of merged branches.
Other good starting points include link:everyday.html[Everday GIT
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
introduction to lower-level git commands for advanced users and
developers.
* link:everyday.html[Everday GIT with 20 Commands Or So]
* link:cvs-migration.html[git for CVS users].

View File

@ -5,7 +5,7 @@ DEF_VER=v1.3.GIT
# First try git-describe, then see if there is a version file
# (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');
elif test -f version
then
@ -16,7 +16,7 @@ fi
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
'')
;;

View File

@ -116,7 +116,7 @@ SCRIPT_SH = \
git-bisect.sh git-branch.sh git-checkout.sh \
git-cherry.sh git-clean.sh git-clone.sh git-commit.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-prune.sh git-pull.sh git-rebase.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
PROGRAMS = \
git-apply$X git-cat-file$X \
git-checkout-index$X git-clone-pack$X git-commit-tree$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-checkout-index$X git-clone-pack$X \
git-convert-objects$X git-fetch-pack$X git-fsck-objects$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-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-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-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-upload-pack$X git-verify-pack$X git-write-tree$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-grep$X git-add$X git-rm$X git-rev-list$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
ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
@ -221,7 +222,13 @@ LIB_OBJS = \
BUILTIN_OBJS = \
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-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)
LIBS = $(GITLIBS) -lz
@ -421,6 +428,9 @@ else
ALL_CFLAGS += -Dsockaddr_storage=sockaddr_in6
endif
endif
ifdef NO_INET_NTOP
LIB_OBJS += compat/inet_ntop.o
endif
ifdef NO_ICONV
ALL_CFLAGS += -DNO_ICONV
@ -486,37 +496,43 @@ $(BUILT_INS): git$X
rm -f $@ && ln git$X $@
common-cmds.h: Documentation/git-*.txt
./generate-cmdlist.sh > $@
./generate-cmdlist.sh > $@+
mv $@+ $@
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
rm -f $@
rm -f $@ $@+
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
$@.sh >$@
chmod +x $@
$@.sh >$@+
chmod +x $@+
mv $@+ $@
$(patsubst %.perl,%,$(SCRIPT_PERL)) : % : %.perl
rm -f $@
rm -f $@ $@+
sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
$@.perl >$@
chmod +x $@
$@.perl >$@+
chmod +x $@+
mv $@+ $@
$(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
rm -f $@
rm -f $@ $@+
sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
-e 's|@@GIT_PYTHON_PATH@@|$(GIT_PYTHON_DIR_SQ)|g' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
$@.py >$@
chmod +x $@
$@.py >$@+
chmod +x $@+
mv $@+ $@
git-cherry-pick: git-revert
cp $< $@
cp $< $@+
mv $@+ $@
git-status: git-commit
cp $< $@
cp $< $@+
mv $@+ $@
# These can record GIT_VERSION
git$X git.spec \
@ -628,7 +644,14 @@ install: all
$(MAKE) -C templates install
$(INSTALL) -d -m755 '$(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:
$(MAKE) -C Documentation install
@ -639,7 +662,8 @@ install-doc:
### Maintainer's dist rules
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)
dist: git.spec git-tar-tree
@ -666,7 +690,7 @@ dist-doc:
:
rm -fr .doc-tmp-dir
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 \
man7=../.doc-tmp-dir/man7 \
install
@ -710,4 +734,3 @@ check-docs::
*) echo "no link: $$v";; \
esac ; \
done | sort

View File

@ -149,7 +149,7 @@ static void free_patch(struct patch *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,
unsigned mode, int stage);
@ -178,7 +178,7 @@ static int get_blob_sha1(struct tree *t, const char *pathname,
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,
unsigned mode, int stage)
{

View File

@ -12,6 +12,7 @@
#include "quote.h"
#include "blob.h"
#include "delta.h"
#include "builtin.h"
// --check turns on checking that the working tree matches the
// 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 read_stdin = 1;

View File

@ -7,6 +7,7 @@
#include "exec_cmd.h"
#include "tag.h"
#include "tree.h"
#include "builtin.h"
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;
}
int main(int argc, char **argv)
int cmd_cat_file(int argc, const char **argv, char **envp)
{
unsigned char sha1[20];
char type[20];
@ -140,7 +141,7 @@ int main(int argc, char **argv)
/* custom pretty-print here */
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);
if (!buf)

View File

@ -6,6 +6,7 @@
#include "cache.h"
#include "commit.h"
#include "tree.h"
#include "builtin.h"
#define BLOCKING (1ul << 14)
@ -76,7 +77,7 @@ static int new_parent(int idx)
return 1;
}
int main(int argc, char **argv)
int cmd_commit_tree(int argc, const char **argv, char **envp)
{
int i;
int parents = 0;
@ -98,7 +99,7 @@ int main(int argc, char **argv)
check_valid(tree_sha1, tree_type);
for (i = 2; i < argc; i += 2) {
char *a, *b;
const char *a, *b;
a = argv[i]; b = argv[i+1];
if (!b || strcmp(a, "-p"))
usage(commit_tree_usage);

View File

@ -7,12 +7,13 @@
#include "diff.h"
#include "commit.h"
#include "revision.h"
#include "builtin.h"
static const char diff_files_usage[] =
"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
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;
int silent = 0;

View File

@ -2,13 +2,14 @@
#include "diff.h"
#include "commit.h"
#include "revision.h"
#include "builtin.h"
static const char diff_cache_usage[] =
"git-diff-index [-m] [--cached] "
"[<common diff options>] <tree-ish> [<path>...]"
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;
int cached = 0;

View File

@ -4,6 +4,7 @@
#include "cache.h"
#include "diff.h"
#include "builtin.h"
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;
const char *prefix = setup_git_directory();

View File

@ -2,6 +2,7 @@
#include "diff.h"
#include "commit.h"
#include "log-tree.h"
#include "builtin.h"
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"
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;
char line[1000];

View File

@ -233,7 +233,7 @@ static int builtin_diff_combined(struct rev_info *revs,
return 0;
}
static void add_head(struct rev_info *revs)
void add_head(struct rev_info *revs)
{
unsigned char sha1[20];
struct object *obj;

View File

@ -518,7 +518,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
argc = nr;
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
const char *name;
char *name;
if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode)))
continue;
if (!pathspec_matches(paths, ce->name))

View File

@ -9,6 +9,10 @@
#include "diff.h"
#include "revision.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,
struct rev_info *rev)
@ -74,3 +78,190 @@ int cmd_log(int argc, const char **argv, char **envp)
rev.diffopt.recursive = 1;
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;
}

View File

@ -10,6 +10,7 @@
#include "cache.h"
#include "quote.h"
#include "dir.h"
#include "builtin.h"
static int abbrev = 0;
static int show_deleted = 0;
@ -321,7 +322,7 @@ static const char ls_files_usage[] =
"[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] "
"[--] [<file>]*";
int main(int argc, const char **argv)
int cmd_ls_files(int argc, const char **argv, char** envp)
{
int i;
int exc_given = 0;

View File

@ -7,6 +7,7 @@
#include "blob.h"
#include "tree.h"
#include "quote.h"
#include "builtin.h"
static int line_termination = '\n';
#define LS_RECURSIVE 1
@ -15,7 +16,7 @@ static int line_termination = '\n';
#define LS_NAME_ONLY 8
static int abbrev = 0;
static int ls_options = 0;
const char **pathspec;
static const char **pathspec;
static int chomp_prefix = 0;
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)
{
int retval = 0;
@ -84,7 +85,7 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen,
return retval;
}
int main(int argc, const char **argv)
int cmd_ls_tree(int argc, const char **argv, char **envp)
{
unsigned char sha1[20];
struct tree *tree;

View File

@ -10,8 +10,10 @@
#include "object.h"
#include "tree.h"
#include "cache-tree.h"
#include "tree-walk.h"
#include <sys/time.h>
#include <signal.h>
#include "builtin.h"
static int reset = 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);
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 len2 = strlen(name2);
@ -66,7 +68,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
int src_size = len + 1;
do {
int i;
char *first;
const char *first;
int firstdir = 0;
int pathlen;
unsigned ce_size;
@ -160,9 +162,10 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
}
if (posns[i]->directory) {
struct tree *tree = lookup_tree(posns[i]->sha1);
any_dirs = 1;
parse_tree(posns[i]->item.tree);
subposns[i] = posns[i]->item.tree->entries;
parse_tree(tree);
subposns[i] = create_tree_entry_list(tree);
posns[i] = posns[i]->next;
src[i + merge] = &df_conflict_entry;
continue;
@ -186,7 +189,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
any_files = 1;
memcpy(ce->sha1, posns[i]->item.any->sha1, 20);
memcpy(ce->sha1, posns[i]->sha1, 20);
src[i + merge] = ce;
subposns[i] = &df_conflict_list;
posns[i] = posns[i]->next;
@ -367,7 +370,7 @@ static int unpack_trees(merge_fn_t fn)
if (len) {
posns = xmalloc(len * sizeof(struct tree_entry_list *));
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;
}
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)
{
struct tree_entry_list *ent;
int cnt;
struct tree_desc desc;
int cnt = 0;
memcpy(it->sha1, tree->object.sha1, 20);
for (cnt = 0, ent = tree->entries; ent; ent = ent->next) {
if (!ent->directory)
desc.buf = tree->buffer;
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++;
else {
struct cache_tree_sub *sub;
struct tree *subtree = (struct tree *)ent->item.tree;
struct tree *subtree;
subtree = lookup_tree(sha1);
if (!subtree->object.parsed)
parse_tree(subtree);
sub = cache_tree_sub(it, ent->name);
sub = cache_tree_sub(it, name);
sub->cache_tree = cache_tree();
prime_cache_tree_rec(sub->cache_tree, subtree);
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;
int main(int argc, char **argv)
int cmd_read_tree(int argc, const char **argv, char **envp)
{
int i, newfd, stage = 0;
unsigned char sha1[20];

View File

@ -85,7 +85,7 @@ static void show_commit(struct commit *commit)
static char pretty_header[16384];
pretty_print_commit(revs.commit_format, commit, ~0,
pretty_header, sizeof(pretty_header),
revs.abbrev);
revs.abbrev, NULL, NULL);
printf("%s%c", pretty_header, hdr_termination);
}
fflush(stdout);
@ -103,6 +103,7 @@ static struct object_list **process_blob(struct blob *blob,
if (obj->flags & (UNINTERESTING | SEEN))
return p;
obj->flags |= SEEN;
name = strdup(name);
return add_object(obj, p, path, name);
}
@ -112,7 +113,7 @@ static struct object_list **process_tree(struct tree *tree,
const char *name)
{
struct object *obj = &tree->object;
struct tree_entry_list *entry;
struct tree_desc desc;
struct name_path me;
if (!revs.tree_objects)
@ -122,21 +123,30 @@ static struct object_list **process_tree(struct tree *tree,
if (parse_tree(tree) < 0)
die("bad tree object %s", sha1_to_hex(obj->sha1));
obj->flags |= SEEN;
name = strdup(name);
p = add_object(obj, p, path, name);
me.up = path;
me.elem = name;
me.elem_len = strlen(name);
entry = tree->entries;
tree->entries = NULL;
while (entry) {
struct tree_entry_list *next = entry->next;
if (entry->directory)
p = process_tree(entry->item.tree, p, &me, entry->name);
desc.buf = tree->buffer;
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))
p = process_tree(lookup_tree(sha1), p, &me, name);
else
p = process_blob(entry->item.blob, p, &me, entry->name);
free(entry);
entry = next;
p = process_blob(lookup_blob(sha1), p, &me, name);
}
free(tree->buffer);
tree->buffer = NULL;
return p;
}

View File

@ -3,13 +3,14 @@
#include "cache.h"
#include "commit.h"
#include "refs.h"
#include "builtin.h"
static const char show_branch_usage[] =
"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_alloc = 0;
static char **default_arg = NULL;
static const char **default_arg = NULL;
#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;
if (commit->object.parsed)
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
pretty, sizeof(pretty), 0);
pretty, sizeof(pretty), 0, NULL, NULL);
else
strcpy(pretty, "(unavailable)");
if (!strncmp(pretty, "[PATCH] ", 8))
@ -548,7 +549,7 @@ static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
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_list *list = NULL, *seen = NULL;
@ -581,7 +582,7 @@ int main(int ac, char **av)
}
while (1 < ac && av[1][0] == '-') {
char *arg = av[1];
const char *arg = av[1];
if (!strcmp(arg, "--")) {
ac--; av++;
break;

View File

@ -7,11 +7,14 @@
#include "commit.h"
#include "strbuf.h"
#include "tar.h"
#include "builtin.h"
#include "pkt-line.h"
#define RECORDSIZE (512)
#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 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];
struct commit *commit;
@ -348,3 +351,58 @@ int main(int argc, char **argv)
free(current_path.buf);
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
View 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;
}

View File

@ -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_log(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_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_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_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

View File

@ -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_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 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_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);
@ -259,6 +260,7 @@ extern void *read_object_with_reference(const unsigned char *sha1,
unsigned char *sha1_ret);
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);
void datestamp(char *buf, int bufsize);
unsigned long approxidate(const char *);

134
commit.c
View File

@ -30,6 +30,7 @@ struct cmt_fmt_map {
{ "raw", 1, CMIT_FMT_RAW },
{ "medium", 1, CMIT_FMT_MEDIUM },
{ "short", 1, CMIT_FMT_SHORT },
{ "email", 1, CMIT_FMT_EMAIL },
{ "full", 5, CMIT_FMT_FULL },
{ "fuller", 5, CMIT_FMT_FULLER },
{ "oneline", 1, CMIT_FMT_ONELINE },
@ -421,6 +422,46 @@ static int get_one_line(const char *msg, unsigned long len)
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)
{
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);
tz = strtol(date, NULL, 10);
ret = sprintf(buf, "%s: %.*s%.*s\n", what,
(fmt == CMIT_FMT_FULLER) ? 4 : 0,
filler, namelen, line);
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,
(fmt == CMIT_FMT_FULLER) ? 4 : 0,
filler, namelen, line);
}
switch (fmt) {
case CMIT_FMT_MEDIUM:
ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz));
break;
case CMIT_FMT_EMAIL:
ret += sprintf(buf + ret, "Date: %s\n",
show_rfc2822_date(time, tz));
break;
case CMIT_FMT_FULLER:
ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz));
break;
@ -455,10 +518,12 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
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]))
len--;
*len_p = 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;
int offset;
if ((fmt == CMIT_FMT_ONELINE) || !parent || !parent->next)
if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
!parent || !parent->next)
return 0;
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;
}
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;
unsigned long offset = 0;
int indent = (fmt == CMIT_FMT_ONELINE) ? 0 : 4;
int indent = 4;
int parents_shown = 0;
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 (;;) {
const char *line = msg;
@ -516,7 +599,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
if (hdr) {
if (linelen == 1) {
hdr = 0;
if (fmt != CMIT_FMT_ONELINE)
if ((fmt != CMIT_FMT_ONELINE) && !subject)
buf[offset++] = '\n';
continue;
}
@ -554,20 +637,47 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
continue;
}
if (is_empty_line(line, linelen)) {
if (is_empty_line(line, &linelen)) {
if (!body)
continue;
if (subject)
continue;
if (fmt == CMIT_FMT_SHORT)
break;
} else {
body = 1;
}
memset(buf + offset, ' ', indent);
memcpy(buf + offset + indent, line, linelen);
offset += linelen + indent;
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);
memcpy(buf + offset + indent, line, linelen);
offset += linelen + indent;
}
buf[offset++] = '\n';
if (fmt == CMIT_FMT_ONELINE)
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]))
offset--;

View File

@ -45,12 +45,13 @@ enum cmit_fmt {
CMIT_FMT_FULL,
CMIT_FMT_FULLER,
CMIT_FMT_ONELINE,
CMIT_FMT_EMAIL,
CMIT_FMT_UNSPECIFIED,
};
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
* of its parents.

200
compat/inet_ntop.c Normal file
View 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 */
}

View File

@ -100,7 +100,7 @@ int path_match(const char *path, int nr, char **match)
if (pathlen > len && path[pathlen - len - 1] != '/')
continue;
*s = 0;
return 1;
return (i + 1);
}
return 0;
}

View File

@ -30,6 +30,7 @@ git-svn.html : git-svn.txt
-f ../../Documentation/asciidoc.conf $<
test: git-svn
cd t && $(SHELL) ./t0000-contrib-git-svn.sh
cd t && $(SHELL) ./t0001-contrib-git-svn-props.sh
clean:
rm -f git-svn *.xml *.html *.1

View File

@ -8,7 +8,7 @@ use vars qw/ $AUTHOR $VERSION
$GIT_SVN_INDEX $GIT_SVN
$GIT_DIR $REV_DIR/;
$AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
$VERSION = '1.0.0';
$VERSION = '1.1.0-pre';
use Cwd qw/abs_path/;
$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,
'branch|b=s' => \@_branch_from,
'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 = (
fetch => [ \&fetch, "Download new revisions from SVN",
{ 'revision|r=s' => \$_revision, %fc_opts } ],
@ -207,7 +211,7 @@ sub rebuild {
push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
sys(@svn_up,"-r$newest_rev");
$ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX;
git_addremove();
index_changes();
exec('git-write-tree');
}
waitpid $pid, 0;
@ -249,7 +253,7 @@ sub fetch {
chdir $SVN_WC or croak $!;
read_uuid();
$last_commit = git_commit($base, @parents);
assert_svn_wc_clean($base->{revision}, $last_commit);
assert_tree($last_commit);
} else {
chdir $SVN_WC or croak $!;
read_uuid();
@ -259,16 +263,20 @@ sub fetch {
push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
my $last = $base;
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}) {
croak "Out of order: last >= current: ",
"$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}");
$last_commit = git_commit($log_msg, $last_commit, @parents);
$last = $log_msg;
}
assert_svn_wc_clean($last->{revision}, $last_commit);
unless (-e "$GIT_DIR/refs/heads/master") {
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);
}
print "Done committing ",scalar @revs," revisions to SVN\n";
}
sub show_ignore {
@ -367,13 +374,11 @@ sub setup_git_svn {
}
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 "$treeish is not a sha1!\n" unless ($treeish =~ /^$sha1$/o);
my $lcr = svn_info('.')->{'Last Changed Rev'};
if ($svn_rev != $lcr) {
print STDERR "Checking for copy-tree ... ";
# use
my @diff = grep(/^Index: /,(safe_qx(qw(svn diff),
"-r$lcr:$svn_rev")));
if (@diff) {
@ -389,7 +394,6 @@ sub assert_svn_wc_clean {
print STDERR $_ foreach @status;
croak;
}
assert_tree($treeish);
}
sub assert_tree {
@ -416,7 +420,7 @@ sub assert_tree {
unlink $tmpindex or croak $!;
}
$ENV{GIT_INDEX_FILE} = $tmpindex;
git_addremove();
index_changes(1);
chomp(my $tree = `git-write-tree`);
if ($old_index) {
$ENV{GIT_INDEX_FILE} = $old_index;
@ -426,6 +430,7 @@ sub assert_tree {
if ($tree ne $expected) {
croak "Tree mismatch, Got: $tree, Expected: $expected\n";
}
unlink $tmpindex;
}
sub parse_diff_tree {
@ -562,7 +567,8 @@ sub precommit_check {
sub svn_checkout_tree {
my ($svn_rev, $treeish) = @_;
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";
my $pid = open my $diff_fh, '-|';
defined $pid or croak $!;
@ -852,13 +858,75 @@ sub svn_info {
sub sys { system(@_) == 0 or croak $? }
sub git_addremove {
system( "git-diff-files --name-only -z ".
" | git-update-index --remove -z --stdin && ".
"git-ls-files -z --others ".
"'--exclude-from=$GIT_DIR/$GIT_SVN/info/exclude'".
" | git-update-index --add -z --stdin"
) == 0 or croak $?
sub eol_cp {
my ($from, $to) = @_;
my $es = safe_qx(qw/svn propget svn:eol-style/, $to);
open my $rfd, '<', $from or croak $!;
binmode $rfd or croak $!;
open my $wfd, '>', $to 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 {
@ -936,7 +1004,7 @@ sub git_commit {
defined $pid or croak $!;
if ($pid == 0) {
$ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX;
git_addremove();
index_changes();
chomp(my $tree = `git-write-tree`);
croak if $?;
if (exists $tree_map{$tree}) {

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

View File

@ -3,48 +3,10 @@
# Copyright (c) 2006 Eric Wong
#
PATH=$PWD/../:$PATH
test_description='git-svn tests'
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"
. ./lib-git-svn.sh
mkdir import
cd import
echo foo > foo
@ -55,10 +17,9 @@ mkdir -p bar
echo 'zzz' > bar/zzz
echo '#!/bin/sh' > 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 ..
rm -rf import
test_expect_success \

View 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

View File

@ -425,7 +425,7 @@ class DiffWindow:
class GitView:
""" This is the main class
"""
version = "0.7"
version = "0.8"
def __init__(self, with_diff=0):
self.with_diff = with_diff
@ -449,8 +449,17 @@ class GitView:
self.accel_group = gtk.AccelGroup()
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):
""" Update the bt_sha1 dictionary with the
@ -500,9 +509,9 @@ class GitView:
menu_bar.show()
vbox.pack_start(menu_bar, expand=False, fill=True)
vbox.pack_start(paned, expand=True, fill=True)
self.window.add(vbox)
paned.show()
vbox.show()
return vbox
def construct_top(self):
@ -974,10 +983,15 @@ class GitView:
try:
self.treeview.set_cursor(self.index[revid])
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
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()
@ -987,8 +1001,8 @@ class GitView:
window.set_diff(commit_sha1, parent_sha1, encoding)
self.treeview.grab_focus()
without_diff = 0
if __name__ == "__main__":
without_diff = 0
if (len(sys.argv) > 1 ):
if (sys.argv[1] == "--without-diff"):

View File

@ -25,6 +25,9 @@ OPTIONS
<args>
All the valid option for git-rev-list(1)
Key Bindings:
F5:
To reread references.
EXAMPLES
------
@ -35,4 +38,3 @@ EXAMPLES
gitview --since=2.weeks.ago
Show the changes during the last two weeks

29
date.c
View File

@ -42,18 +42,24 @@ static const char *weekday_names[] = {
* thing, which means that tz -0100 is passed in as the integer -100,
* 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;
static char timebuf[200];
int minutes;
minutes = tz < 0 ? -tz : tz;
minutes = (minutes / 100)*60 + (minutes % 100);
minutes = tz < 0 ? -minutes : minutes;
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)
return NULL;
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;
}
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.
*

136
diff.c
View File

@ -237,7 +237,7 @@ static char *pprint_rename(const char *a, const char *b)
if (a_midlen < 0) a_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",
pfx_length, a,
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 minuses[]= "----------------------------------------------------------------------";
const char mime_boundary_leader[] = "------------";
static void show_stats(struct diffstat_t* data)
{
@ -397,6 +398,46 @@ static void show_stats(struct diffstat_t* data)
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,
unsigned long 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)
{
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);
}
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)
{
memset(options, 0, sizeof(*options));
@ -1205,9 +1300,18 @@ int diff_setup_done(struct diff_options *options)
* recursive bits for other formats here.
*/
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;
/*
* 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)
options->rename_limit = diff_rename_limit_default;
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"))
options->output_format = DIFF_FORMAT_DIFFSTAT;
else if (!strcmp(arg, "--check"))
options->output_format = DIFF_FORMAT_CHECKDIFF;
else if (!strcmp(arg, "--summary"))
options->summary = 1;
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);
}
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)
{
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:
diff_flush_stat(p, options, diffstat);
break;
case DIFF_FORMAT_CHECKDIFF:
diff_flush_checkdiff(p, options);
break;
case DIFF_FORMAT_PATCH:
diff_flush_patch(p, options);
break;
@ -1867,7 +1989,13 @@ void diff_flush(struct diff_options *options)
show_stats(diffstat);
free(diffstat);
diffstat = NULL;
putchar(options->line_termination);
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);
}
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
@ -1880,7 +2008,7 @@ void diff_flush(struct diff_options *options)
}
for (i = 0; i < q->nr; i++) {
if (options->summary)
if (diffstat && options->summary)
diff_summary(q->queue[i]);
diff_free_filepair(q->queue[i]);
}

4
diff.h
View File

@ -44,6 +44,7 @@ struct diff_options {
int rename_limit;
int setup;
int abbrev;
const char *stat_sep;
int nr_paths;
const char **paths;
@ -52,6 +53,8 @@ struct diff_options {
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_release_paths(struct diff_options *);
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_STATUS 5
#define DIFF_FORMAT_DIFFSTAT 6
#define DIFF_FORMAT_CHECKDIFF 7
extern void diff_flush(struct diff_options*);

View File

@ -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)
{
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) {
next = current->next;
if ((!memcmp(current->name, "refs/", 5) &&
check_ref_format(current->name + 5)) ||
(!fetch_all &&
!path_match(current->name, nr_match, match))) {
if (prev == NULL)
*refs = next;
else
prev->next = next;
free(current);
} else
prev = current;
if (nr_match && !fetch_all) {
if (ARRAY_SIZE(fastarray) < nr_match)
return_refs = xcalloc(nr_match, sizeof(struct ref *));
else {
return_refs = fastarray;
memset(return_refs, 0, sizeof(struct ref *) * nr_match);
}
}
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)

36
fetch.c
View File

@ -9,8 +9,6 @@
const char *write_ref = NULL;
const unsigned char *current_ref = NULL;
int get_tree = 0;
int get_history = 0;
int get_all = 0;
@ -43,16 +41,22 @@ static int process_tree(struct tree *tree)
if (parse_tree(tree))
return -1;
entry = tree->entries;
tree->entries = NULL;
entry = create_tree_entry_list(tree);
while (entry) {
struct tree_entry_list *next = entry->next;
if (process(entry->item.any))
return -1;
free(entry->name);
if (entry->directory) {
struct tree *tree = lookup_tree(entry->sha1);
process_tree(tree);
} else {
struct blob *blob = lookup_blob(entry->sha1);
process(&blob->object);
}
free(entry);
entry = next;
}
free(tree->buffer);
tree->buffer = NULL;
return 0;
}
@ -205,19 +209,12 @@ static int mark_complete(const char *path, const unsigned char *sha1)
int pull(char *target)
{
unsigned char sha1[20];
int fd = -1;
save_commit_buffer = 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);
}
if (interpret_target(target, sha1))
return error("Could not interpret %s as something to pull",
@ -227,12 +224,7 @@ int pull(char *target)
if (loop())
return -1;
if (write_ref) {
if (current_ref) {
write_ref_sha1(write_ref, fd, sha1);
} else {
write_ref_sha1_unlocked(write_ref, sha1);
}
}
if (write_ref)
write_ref_sha1_unlocked(write_ref, sha1);
return 0;
}

View File

@ -25,9 +25,6 @@ extern int fetch_ref(char *ref, unsigned char *sha1);
/* If set, the ref filename to write the target value to. */
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. */
extern int get_tree;

View File

@ -11,6 +11,7 @@
#include "cache-tree.h"
#define REACHABLE 0x0001
#define SEEN 0x0002
static int show_root = 0;
static int show_tags = 0;
@ -161,7 +162,7 @@ static int fsck_tree(struct tree *item)
struct tree_entry_list *entry, *last;
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, '/'))
has_full_path = 1;
has_zero_pad |= entry->zeropad;
@ -198,17 +199,15 @@ static int fsck_tree(struct tree *item)
default:
break;
}
free(last->name);
free(last);
}
last = entry;
}
if (last) {
free(last->name);
if (last)
free(last);
}
item->entries = NULL;
free(item->buffer);
item->buffer = NULL;
retval = 0;
if (has_full_path) {
@ -278,6 +277,9 @@ static int fsck_sha1(unsigned char *sha1)
struct object *obj = parse_object(sha1);
if (!obj)
return error("%s: object not found", sha1_to_hex(sha1));
if (obj->flags & SEEN)
return 0;
obj->flags |= SEEN;
if (obj->type == blob_type)
return 0;
if (obj->type == tree_type)
@ -465,6 +467,7 @@ int main(int argc, char **argv)
{
int i, heads;
track_object_refs = 1;
setup_git_directory();
for (i = 1; i < argc; i++) {

View File

@ -37,7 +37,6 @@ show-branch
status
tag
verify-tag
whatchanged
EOF
while read cmd
do

View File

@ -9,7 +9,7 @@
unset CDPATH
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
}
@ -102,6 +102,7 @@ quiet=
local=no
use_local=no
local_shared=no
unset template
no_checkout=
upload_pack=
bare=
@ -120,6 +121,11 @@ while
*,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) use_local=yes ;;
*,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared)
local_shared=yes; use_local=yes ;;
1,--template) usage ;;
*,--template)
shift; template="--template=$1" ;;
*,--template=*)
template="$1" ;;
*,-q|*,--quiet) quiet=-q ;;
*,--use-separate-remote)
use_separate_remote=t ;;
@ -199,11 +205,11 @@ dir="$2"
[ -e "$dir" ] && echo "$dir already exists." && usage
mkdir -p "$dir" &&
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
yes) GIT_DIR="$D" ;;
*) 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
yes)
GIT_DIR="$D" ;;
@ -407,5 +413,5 @@ Pull: refs/heads/$head_points_at:$origin_track" &&
fi
rm -f "$GIT_DIR/CLONE_HEAD" "$GIT_DIR/REMOTE_HEAD"
trap - exit
trap - 0

View File

@ -3,7 +3,7 @@
# Copyright (c) 2005 Linus Torvalds
# 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
. git-sh-setup
@ -134,13 +134,17 @@ run_status () {
report "Changed but not updated" \
"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"
then
git-ls-files -z --others --directory \
git-ls-files -z --others $option \
--exclude-from="$GIT_DIR/info/exclude" \
--exclude-per-directory=.gitignore
else
git-ls-files -z --others --directory \
git-ls-files -z --others $option \
--exclude-per-directory=.gitignore
fi |
perl -e '$/ = "\0";
@ -203,6 +207,7 @@ verbose=
signoff=
force_author=
only_include_assumed=
untracked_files=
while case "$#" in 0) break;; esac
do
case "$1" in
@ -340,6 +345,12 @@ do
verbose=t
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
break
@ -615,6 +626,9 @@ fi
if test -z "$no_edit"
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"
run_status
} >>"$GIT_DIR"/COMMIT_EDITMSG

View File

@ -1,10 +1,16 @@
#!/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 Getopt::Std;
use File::Temp qw(tempdir);
use Data::Dumper;
use File::Basename qw(basename);
use File::Basename qw(basename dirname);
unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
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`;
$? && 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);
#print @files;
$? && die "Error in git-diff-tree";
@ -92,7 +98,14 @@ foreach my $f (@files) {
chomp $f;
my @fields = split(m!\s+!, $f);
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') {
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
my $dirty;
foreach my $d (@dirs) {
if (-e $d) {
$dirty = 1;
warn "$d exists and is not a directory!\n";
}
}
foreach my $f (@afiles) {
# This should return only one value
my @status = grep(m/^File/, safe_pipe_capture('cvs', '-q', 'status' ,$f));
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;
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) {
@ -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";
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`;
chomp $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";
$dirty = 1;
next;

View File

@ -23,13 +23,13 @@ use File::Basename qw(basename dirname);
use Time::Local;
use IO::Socket;
use IO::Pipe;
use POSIX qw(strftime dup2);
use POSIX qw(strftime dup2 ENOENT);
use IPC::Open2;
$SIG{'PIPE'}="IGNORE";
$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);
sub usage() {
@ -85,7 +85,7 @@ sub write_author_info($) {
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;
@ARGV <= 1 or usage();
@ -315,15 +315,7 @@ sub _line {
chomp $cnt;
die "Duh: Filesize $cnt" if $cnt !~ /^\d+$/;
$line="";
$res=0;
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;
}
$res = $self->_fetchfile($fh, $cnt);
} elsif($line =~ s/^ //) {
print $fh $line;
$res += length($line);
@ -335,14 +327,7 @@ sub _line {
chomp $cnt;
die "Duh: Mbinary $cnt" if $cnt !~ /^\d+$/ or $cnt<1;
$line="";
while($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;
}
$res += $self->_fetchfile($fh, $cnt);
} else {
chomp $line;
if($line eq "ok") {
@ -384,6 +369,23 @@ sub file {
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;
@ -429,21 +431,24 @@ sub getwd() {
return $pwd;
}
sub get_headref($$) {
my $name = shift;
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 is_sha1 {
my $s = shift;
return $s =~ /^[a-f0-9]{40}$/;
}
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
or mkdir($git_tree,0777)
@ -561,98 +566,66 @@ unless($pid) {
my $state = 0;
my($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg);
my(@old,@new,@skipped);
my $commit = sub {
my $pid;
while(@old) {
my @o2;
if(@old > 55) {
@o2 = splice(@old,0,50);
} else {
@o2 = @old;
@old = ();
}
system("git-update-index","--force-remove","--",@o2);
die "Cannot remove files: $?\n" if $?;
}
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 $?;
}
sub update_index (\@\@) {
my $old = shift;
my $new = shift;
open(my $fh, '|-', qw(git-update-index -z --index-info))
or die "unable to open git-update-index: $!";
print $fh
(map { "0 0000000000000000000000000000000000000000\t$_\0" }
@$old),
(map { '100' . sprintf('%o', $_->[0]) . " $_->[1]\t$_->[2]\0" }
@$new)
or die "unable to write to git-update-index: $!";
close $fh
or die "unable to write to git-update-index: $!";
$? and die "git-update-index reported error: $?";
}
$pid = open(C,"-|");
die "Cannot fork: $!" unless defined $pid;
unless($pid) {
exec("git-write-tree");
die "Cannot exec git-write-tree: $!\n";
}
chomp(my $tree = <C>);
length($tree) == 40
or die "Cannot get tree id ($tree): $!\n";
close(C)
sub write_tree () {
open(my $fh, '-|', qw(git-write-tree))
or die "unable to open git-write-tree: $!";
chomp(my $tree = <$fh>);
is_sha1($tree)
or die "Cannot get tree id ($tree): $!";
close($fh)
or die "Error running git-write-tree: $?\n";
print "Tree ID $tree\n" if $opt_v;
return $tree;
}
my $parent = "";
if(open(C,"$git_dir/refs/heads/$last_branch")) {
chomp($parent = <C>);
close(C);
length($parent) == 40
or die "Cannot get parent id ($parent): $!\n";
print "Parent ID $parent\n" if $opt_v;
}
my($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg);
my(@old,@new,@skipped);
sub commit {
update_index(@old, @new);
@old = @new = ();
my $tree = write_tree();
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 $pw = IO::Pipe->new() or die "Cannot open pipe: $!\n";
$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 @commit_args;
push @commit_args, ("-p", $parent) if $parent;
my @par = ();
@par = ("-p",$parent) if $parent;
# loose detection of merges
# based on the commit msg
foreach my $rx (@mergerx) {
if ($logmsg =~ $rx) {
my $mparent = $1;
if ($mparent eq 'HEAD') { $mparent = $opt_o };
if ( -e "$git_dir/refs/heads/$mparent") {
$mparent = get_headref($mparent, $git_dir);
push @par, '-p', $mparent;
print OUT "Merge parent branch: $mparent\n" if $opt_v;
}
}
# loose detection of merges
# based on the commit msg
foreach my $rx (@mergerx) {
next unless $logmsg =~ $rx && $1;
my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
if(my $sha1 = get_headref($mparent, $git_dir)) {
push @commit_args, '-p', $mparent;
print "Merge parent branch: $mparent\n" if $opt_v;
}
exec("env",
"GIT_AUTHOR_NAME=$author_name",
"GIT_AUTHOR_EMAIL=$author_email",
"GIT_AUTHOR_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
"GIT_COMMITTER_NAME=$author_name",
"GIT_COMMITTER_EMAIL=$author_email",
"GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
"git-commit-tree", $tree,@par);
die "Cannot exec git-commit-tree: $!\n";
}
$pw->writer();
$pr->reader();
my $commit_date = strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date));
$ENV{GIT_AUTHOR_NAME} = $author_name;
$ENV{GIT_AUTHOR_EMAIL} = $author_email;
$ENV{GIT_AUTHOR_DATE} = $commit_date;
$ENV{GIT_COMMITTER_NAME} = $author_name;
$ENV{GIT_COMMITTER_EMAIL} = $author_email;
$ENV{GIT_COMMITTER_DATE} = $commit_date;
my $pid = open2(my $commit_read, my $commit_write,
'git-commit-tree', $tree, @commit_args);
# compatibility with git2cvs
substr($logmsg,32767) = "" if length($logmsg) > 32767;
@ -661,18 +634,17 @@ my $commit = sub {
if (@skipped) {
$logmsg .= "\n\n\nSKIPPED:\n\t";
$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";
$pw->close();
print "Committed patch $patchset ($branch ".strftime("%Y-%m-%d %H:%M:%S",gmtime($date)).")\n" if $opt_v;
chomp(my $cid = <$pr>);
length($cid) == 40
or die "Cannot get commit id ($cid): $!\n";
print "Committed patch $patchset ($branch $commit_date)\n" if $opt_v;
chomp(my $cid = <$commit_read>);
is_sha1($cid) or die "Cannot get commit id ($cid): $!\n";
print "Commit ID $cid\n" if $opt_v;
$pr->close();
close($commit_read);
waitpid($pid,0);
die "Error running git-commit-tree: $?\n" if $?;
@ -716,6 +688,7 @@ my $commit = sub {
}
};
my $commitcount = 1;
while(<CVS>) {
chomp;
if($state == 0 and /^-+$/) {
@ -849,7 +822,14 @@ while(<CVS>) {
} elsif($state == 9 and /^\s*$/) {
$state = 10;
} 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;
} elsif($state == 11 and /^-+$/) {
$state = 1;
@ -859,7 +839,7 @@ while(<CVS>) {
print "* UNKNOWN LINE * $_\n";
}
}
&$commit() if $branch and $state != 11;
commit() if $branch and $state != 11;
unlink($git_index);

View File

@ -211,12 +211,12 @@ esac
reflist=$(get_remote_refs_for_fetch "$@")
if test "$tags"
then
taglist=$(IFS=" " &&
taglist=`IFS=" " &&
git-ls-remote $upload_pack --tags "$remote" |
while read sha1 name
do
case "$name" in
(*^*) continue ;;
*^*) continue ;;
esac
if git-check-ref-format "$name"
then
@ -224,7 +224,7 @@ then
else
echo >&2 "warning: tag ${name} ignored"
fi
done)
done`
if test "$#" -gt 1
then
# remote URL plus explicit refspecs; we need to merge them.

View File

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

View File

@ -58,11 +58,19 @@ http://* | https://* )
;;
rsync://* )
mkdir $tmpdir
mkdir $tmpdir &&
rsync -rlq "$peek_repo/HEAD" $tmpdir &&
rsync -rq "$peek_repo/refs" $tmpdir || {
echo "failed slurping"
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) |
while read path
do

View File

@ -152,6 +152,6 @@ then
exit 0
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"

17
git.c
View File

@ -47,6 +47,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "whatchanged", cmd_whatchanged },
{ "show", cmd_show },
{ "push", cmd_push },
{ "format-patch", cmd_format_patch },
{ "count-objects", cmd_count_objects },
{ "diff", cmd_diff },
{ "grep", cmd_grep },
@ -54,7 +55,21 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "add", cmd_add },
{ "rev-list", cmd_rev_list },
{ "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;

View File

@ -1704,6 +1704,7 @@ static struct object_list **process_blob(struct blob *blob,
return p;
obj->flags |= SEEN;
name = strdup(name);
return add_object(obj, p, path, name);
}
@ -1713,7 +1714,7 @@ static struct object_list **process_tree(struct tree *tree,
const char *name)
{
struct object *obj = &tree->object;
struct tree_entry_list *entry;
struct tree_desc desc;
struct name_path me;
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));
obj->flags |= SEEN;
name = strdup(name);
p = add_object(obj, p, NULL, name);
me.up = path;
me.elem = name;
me.elem_len = strlen(name);
entry = tree->entries;
tree->entries = NULL;
while (entry) {
struct tree_entry_list *next = entry->next;
if (entry->directory)
p = process_tree(entry->item.tree, p, &me, entry->name);
desc.buf = tree->buffer;
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))
p = process_tree(lookup_tree(sha1), p, &me, name);
else
p = process_blob(entry->item.blob, p, &me, entry->name);
free(entry);
entry = next;
p = process_blob(lookup_blob(sha1), p, &me, name);
}
free(tree->buffer);
tree->buffer = NULL;
return p;
}

View File

@ -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;
const char *extra;
int len;
char *subject = NULL, *after_subject = NULL;
opt->loginfo = NULL;
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..
*/
printf("%s%s",
opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
diff_unique_abbrev(commit->object.sha1, abbrev_commit));
if (opt->parents)
show_parents(commit, abbrev_commit);
if (parent)
printf(" (from %s)", diff_unique_abbrev(parent->object.sha1, abbrev_commit));
putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
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",
opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
diff_unique_abbrev(commit->object.sha1, abbrev_commit));
if (opt->parents)
show_parents(commit, abbrev_commit);
if (parent)
printf(" (from %s)",
diff_unique_abbrev(parent->object.sha1,
abbrev_commit));
putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
}
/*
* 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);
}
@ -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)
{
struct log_info log;
int shown;
log.commit = commit;
log.parent = NULL;
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;
show_log(opt, opt->loginfo, "");
shown = 1;
}
opt->loginfo = NULL;
return 0;
return shown;
}

View File

@ -72,11 +72,14 @@ static int bogus_from(char *line)
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;
strcpy(line, in_line);
at = strchr(line, '@');
if (!at)
return bogus_from(line);
@ -237,38 +240,46 @@ static int eatspace(char *line)
#define SEEN_FROM 01
#define SEEN_DATE 02
#define SEEN_SUBJECT 04
#define SEEN_BOGUS_UNIX_FROM 010
#define SEEN_PREFIX 020
/* 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 (!(*seen & SEEN_FROM) && handle_from(line+6)) {
*seen |= SEEN_FROM;
return 1;
return;
}
}
if (!memcmp("Date:", line, 5) && isspace(line[5])) {
if (!(*seen & SEEN_DATE)) {
handle_date(line+6);
*seen |= SEEN_DATE;
return 1;
return;
}
}
if (!memcmp("Subject:", line, 8) && isspace(line[8])) {
if (!(*seen & SEEN_SUBJECT)) {
handle_subject(line+9);
*seen |= SEEN_SUBJECT;
return 1;
return;
}
}
if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
if (!(*seen & SEEN_SUBJECT)) {
handle_subject(line);
*seen |= SEEN_SUBJECT;
return 1;
return;
}
}
return 0;
*seen |= SEEN_PREFIX;
}
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 *);
struct header_def {
const char *name;
@ -331,7 +343,7 @@ struct header_def {
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;
@ -343,13 +355,17 @@ static void check_header(char *line, int len, struct header_def *header)
int len = header[i].namelen;
if (!strncasecmp(line, header[i].name, len) &&
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);
break;
}
}
}
static void check_subheader_line(char *line, int len)
static void check_subheader_line(char *line)
{
static struct header_def header[] = {
{ "Content-Type", handle_subcontent_type },
@ -357,9 +373,9 @@ static void check_subheader_line(char *line, int len)
handle_content_transfer_encoding },
{ 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[] = {
{ "From", handle_from },
@ -370,7 +386,30 @@ static void check_header_line(char *line, int len)
handle_content_transfer_encoding },
{ 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)
@ -379,18 +418,25 @@ static int read_one_header_line(char *line, int sz, FILE *in)
while (ofs < sz) {
int peek, len;
if (fgets(line + ofs, sz - ofs, in) == NULL)
return ofs;
break;
len = eatspace(line + ofs);
if (len == 0)
return ofs;
peek = fgetc(in); ungetc(peek, in);
if (peek == ' ' || peek == '\t') {
/* Yuck, 2822 header "folding" */
ofs += len;
continue;
break;
if (!is_rfc2822_header(line)) {
/* Re-add the newline */
line[ofs + len] = '\n';
line[ofs + len + 1] = '\0';
break;
}
return ofs + len;
ofs += len;
/* Yuck, 2822 header "folding" */
peek = fgetc(in); ungetc(peek, in);
if (peek != ' ' && peek != '\t')
break;
}
/* Count mbox From headers as headers */
if (!ofs && !memcmp(line, "From ", 5))
ofs = 1;
return ofs;
}
@ -585,25 +631,13 @@ static void decode_transfer_encoding(char *line)
static void handle_info(void)
{
char *sub;
static int done_info = 0;
if (done_info)
return;
done_info = 1;
sub = cleanup_subject(subject);
cleanup_space(name);
cleanup_space(date);
cleanup_space(email);
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",
name, email, sub, date);
}
@ -611,7 +645,7 @@ static void handle_info(void)
/* We are inside message body and have read line[] already.
* Spit out the commit log.
*/
static int handle_commit_msg(void)
static int handle_commit_msg(int *seen)
{
if (!cmitmsg)
return 0;
@ -635,6 +669,11 @@ static int handle_commit_msg(void)
decode_transfer_encoding(line);
if (metainfo_charset)
convert_to_utf8(line, charset);
handle_inbody_header(seen, line);
if (!(*seen & SEEN_PREFIX))
continue;
fputs(line, cmitmsg);
} while (fgets(line, sizeof(line), stdin) != NULL);
fclose(cmitmsg);
@ -666,26 +705,16 @@ static void handle_patch(void)
* that the first part to contain commit message and a patch, and
* 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 len;
while (fgets(line, sizeof(line), stdin) != NULL) {
again:
len = eatspace(line);
n++;
if (!len)
continue;
if (is_multipart_boundary(line))
break;
if (0 <= seen && handle_inbody_header(&seen, line))
continue;
seen = -1; /* no more inbody headers */
line[len] = '\n';
handle_info();
if (handle_commit_msg())
if (handle_commit_msg(seen))
goto again;
handle_patch();
break;
@ -697,6 +726,7 @@ static int handle_multipart_one_part(void)
static void handle_multipart_body(void)
{
int seen = 0;
int part_num = 0;
/* Skip up to the first boundary */
@ -709,16 +739,16 @@ static void handle_multipart_body(void)
return;
/* We are on boundary line. Start slurping the subhead. */
while (1) {
int len = read_one_header_line(line, sizeof(line), stdin);
if (!len) {
if (handle_multipart_one_part() < 0)
int hdr = read_one_header_line(line, sizeof(line), stdin);
if (!hdr) {
if (handle_multipart_one_part(&seen) < 0)
return;
/* Reset per part headers */
transfer_encoding = TE_DONTCARE;
charset[0] = 0;
}
else
check_subheader_line(line, len);
check_subheader_line(line);
}
fclose(patchfile);
if (!patch_lines) {
@ -732,18 +762,9 @@ static void handle_body(void)
{
int seen = 0;
while (fgets(line, sizeof(line), stdin) != NULL) {
int len = eatspace(line);
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();
if (line[0] || fgets(line, sizeof(line), stdin) != NULL) {
handle_commit_msg(&seen);
handle_patch();
break;
}
fclose(patchfile);
if (!patch_lines) {
@ -787,15 +808,16 @@ int main(int argc, char **argv)
exit(1);
}
while (1) {
int len = read_one_header_line(line, sizeof(line), stdin);
if (!len) {
int hdr = read_one_header_line(line, sizeof(line), stdin);
if (!hdr) {
if (multipart_boundary[0])
handle_multipart_body();
else
handle_body();
handle_info();
break;
}
check_header_line(line, len);
check_header_line(line);
}
return 0;
}

45
mktag.c
View File

@ -45,42 +45,46 @@ static int verify_tag(char *buffer, unsigned long size)
unsigned char sha1[20];
const char *object, *type_line, *tag_line, *tagger_line;
if (size < 64 || size > MAXSIZE-1)
return -1;
if (size < 64)
return error("wanna fool me ? you obviously got the size wrong !\n");
buffer[size] = 0;
/* Verify object line */
object = buffer;
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))
return -1;
return error("char%d: could not get SHA1 hash\n", 7);
/* Verify type line */
type_line = object + 48;
if (memcmp(type_line - 1, "\ntype ", 6))
return -1;
return error("char%d: could not find \"\\ntype \"\n", 47);
/* Verify tag-line */
tag_line = strchr(type_line, '\n');
if (!tag_line)
return -1;
return error("char%td: could not find next \"\\n\"\n", type_line - buffer);
tag_line++;
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 */
typelen = tag_line - type_line - strlen("type \n");
if (typelen >= sizeof(type))
return -1;
return error("char%td: type too long\n", type_line+5 - buffer);
memcpy(type, type_line+5, typelen);
type[typelen] = 0;
/* Verify that the object matches */
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))
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 */
tag_line += 4;
@ -90,14 +94,14 @@ static int verify_tag(char *buffer, unsigned long size)
break;
if (c > ' ')
continue;
return -1;
return error("char%td: could not verify tag name\n", tag_line - buffer);
}
/* Verify the tagger line */
tagger_line = tag_line;
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.. */
return 0;
@ -105,8 +109,8 @@ static int verify_tag(char *buffer, unsigned long size)
int main(int argc, char **argv)
{
unsigned long size;
char buffer[MAXSIZE];
unsigned long size = 4096;
char *buffer = malloc(size);
unsigned char result_sha1[20];
if (argc != 1)
@ -114,13 +118,9 @@ int main(int argc, char **argv)
setup_git_directory();
// Read the signature
size = 0;
for (;;) {
int ret = xread(0, buffer + size, MAXSIZE - size);
if (ret <= 0)
break;
size += ret;
if (read_pipe(0, &buffer, &size)) {
free(buffer);
die("could not read from stdin");
}
// 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)
die("unable to write tag file");
free(buffer);
printf("%s\n", sha1_to_hex(result_sha1));
return 0;
}

View File

@ -9,7 +9,7 @@ struct object **objs;
static int nr_objs;
int obj_allocs;
int track_object_refs = 1;
int track_object_refs = 0;
static int hashtable_index(const unsigned char *sha1)
{
@ -200,8 +200,11 @@ struct object *parse_object(const unsigned char *sha1)
obj = &blob->object;
} else if (!strcmp(type, tree_type)) {
struct tree *tree = lookup_tree(sha1);
parse_tree_buffer(tree, buffer, size);
obj = &tree->object;
if (!tree->object.parsed) {
parse_tree_buffer(tree, buffer, size);
buffer = NULL;
}
} else if (!strcmp(type, commit_type)) {
struct commit *commit = lookup_commit(sha1);
parse_commit_buffer(commit, buffer, size);

View File

@ -108,7 +108,8 @@ static int get_value(const char* key_, const char* regex_)
int main(int argc, const char **argv)
{
setup_git_directory();
int nongit = 0;
setup_git_directory_gently(&nongit);
while (1 < argc) {
if (!strcmp(argv[1], "--int"))

View File

@ -63,14 +63,13 @@ void mark_tree_uninteresting(struct tree *tree)
return;
if (parse_tree(tree) < 0)
die("bad tree %s", sha1_to_hex(obj->sha1));
entry = tree->entries;
tree->entries = NULL;
entry = create_tree_entry_list(tree);
while (entry) {
struct tree_entry_list *next = entry->next;
if (entry->directory)
mark_tree_uninteresting(entry->item.tree);
mark_tree_uninteresting(lookup_tree(entry->sha1));
else
mark_blob_uninteresting(entry->item.blob);
mark_blob_uninteresting(lookup_blob(entry->sha1));
free(entry);
entry = next;
}
@ -733,6 +732,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
revs->abbrev = DEFAULT_ABBREV;
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")) {
revs->abbrev_commit = 1;
continue;

View File

@ -58,6 +58,8 @@ struct rev_info {
unsigned int abbrev;
enum cmit_fmt commit_format;
struct log_info *loginfo;
int nr, total;
const char *mime_boundary;
/* special limits */
int max_count;

View File

@ -1399,6 +1399,25 @@ int move_temp_to_file(const char *tmpfile, char *filename)
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 size;
@ -1465,8 +1484,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
deflateEnd(&stream);
size = stream.total_out;
if (write(fd, compressed, size) != size)
die("unable to write file");
if (write_buffer(fd, compressed, size) < 0)
die("unable to write sha1 file");
fchmod(fd, 0444);
close(fd);
free(compressed);
@ -1474,73 +1493,70 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
return move_temp_to_file(tmpfile, filename);
}
/*
* 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)
{
size_t size;
z_stream stream;
unsigned char *unpacked;
unsigned long len;
char type[20];
char hdr[50];
int hdrlen;
void *buf;
// need to unpack and recompress it by itself
unpacked = read_packed_sha1(sha1, type, &len);
hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
/* Set it up */
memset(&stream, 0, sizeof(stream));
deflateInit(&stream, Z_BEST_COMPRESSION);
size = deflateBound(&stream, len + hdrlen);
buf = xmalloc(size);
/* Compress it */
stream.next_out = buf;
stream.avail_out = size;
/* First header.. */
stream.next_in = (void *)hdr;
stream.avail_in = hdrlen;
while (deflate(&stream, 0) == Z_OK)
/* nothing */;
/* Then the data itself.. */
stream.next_in = unpacked;
stream.avail_in = len;
while (deflate(&stream, Z_FINISH) == Z_OK)
/* nothing */;
deflateEnd(&stream);
free(unpacked);
*objsize = stream.total_out;
return buf;
}
int write_sha1_to_fd(int fd, const unsigned char *sha1)
{
ssize_t size;
int retval;
unsigned long objsize;
int posn = 0;
void *map = map_sha1_file_internal(sha1, &objsize);
void *buf = map;
void *temp_obj = NULL;
z_stream stream;
void *buf = map_sha1_file_internal(sha1, &objsize);
if (!buf) {
unsigned char *unpacked;
unsigned long len;
char type[20];
char hdr[50];
int hdrlen;
// need to unpack and recompress it by itself
unpacked = read_packed_sha1(sha1, type, &len);
hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
/* Set it up */
memset(&stream, 0, sizeof(stream));
deflateInit(&stream, Z_BEST_COMPRESSION);
size = deflateBound(&stream, len + hdrlen);
temp_obj = buf = xmalloc(size);
/* Compress it */
stream.next_out = buf;
stream.avail_out = size;
/* First header.. */
stream.next_in = (void *)hdr;
stream.avail_in = hdrlen;
while (deflate(&stream, 0) == Z_OK)
/* nothing */;
/* Then the data itself.. */
stream.next_in = unpacked;
stream.avail_in = len;
while (deflate(&stream, Z_FINISH) == Z_OK)
/* nothing */;
deflateEnd(&stream);
free(unpacked);
objsize = stream.total_out;
if (buf) {
retval = write_buffer(fd, buf, objsize);
munmap(buf, objsize);
return retval;
}
do {
size = write(fd, buf + posn, objsize - posn);
if (size <= 0) {
if (!size) {
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;
buf = repack_object(sha1, &objsize);
retval = write_buffer(fd, buf, objsize);
free(buf);
return retval;
}
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) -
stream.avail_out);
} 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,
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;
}
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 = malloc(size);
int iret, ret;
char* buf = *return_buf;
unsigned long size = *return_size;
int iret;
unsigned long off = 0;
unsigned char hdr[50];
int hdrlen;
do {
iret = read(fd, buf + off, size - off);
iret = xread(fd, buf + off, size - off);
if (iret > 0) {
off += iret;
if (off == size) {
@ -1663,16 +1688,34 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
}
}
} 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);
return -1;
}
if (!type)
type = blob_type;
if (write_object)
ret = write_sha1_file(buf, off, type, sha1);
ret = write_sha1_file(buf, size, type, sha1);
else {
write_sha1_file_prepare(buf, off, type, sha1, hdr, &hdrlen);
write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen);
ret = 0;
}
free(buf);

View File

@ -75,7 +75,7 @@ test_expect_success \
git-update-index --add yomin &&
git-read-tree -m -u $treeH $treeM &&
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 &&
check_cache_at yomin clean &&
sum bozbar frotz nitfol >actual4.sum &&
@ -94,7 +94,7 @@ test_expect_success \
echo yomin yomin >yomin &&
git-read-tree -m -u $treeH $treeM &&
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 &&
check_cache_at yomin dirty &&
sum bozbar frotz nitfol >actual5.sum &&
@ -112,7 +112,7 @@ test_expect_success \
git-update-index --add frotz &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >6.out &&
diff --unified=0 M.out 6.out &&
diff -U0 M.out 6.out &&
check_cache_at frotz clean &&
sum bozbar frotz nitfol >actual3.sum &&
cmp M.sum actual3.sum &&
@ -129,7 +129,7 @@ test_expect_success \
echo frotz frotz >frotz &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >7.out &&
diff --unified=0 M.out 7.out &&
diff -U0 M.out 7.out &&
check_cache_at frotz dirty &&
sum bozbar frotz nitfol >actual7.sum &&
if cmp M.sum actual7.sum; then false; else :; fi &&
@ -206,7 +206,7 @@ test_expect_success \
git-update-index --add nitfol &&
git-read-tree -m -u $treeH $treeM &&
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 &&
sum bozbar frotz >actual14.sum &&
grep -v nitfol M.sum > expected14.sum &&
@ -227,7 +227,7 @@ test_expect_success \
echo nitfol nitfol nitfol >nitfol &&
git-read-tree -m -u $treeH $treeM &&
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 &&
check_cache_at nitfol dirty &&
sum bozbar frotz >actual15.sum &&
@ -264,7 +264,7 @@ test_expect_success \
git-update-index --add bozbar &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >18.out &&
diff --unified=0 M.out 18.out &&
diff -U0 M.out 18.out &&
check_cache_at bozbar clean &&
sum bozbar frotz nitfol >actual18.sum &&
cmp M.sum actual18.sum'
@ -278,7 +278,7 @@ test_expect_success \
echo gnusto gnusto >bozbar &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >19.out &&
diff --unified=0 M.out 19.out &&
diff -U0 M.out 19.out &&
check_cache_at bozbar dirty &&
sum frotz nitfol >actual19.sum &&
grep -v bozbar M.sum > expected19.sum &&
@ -297,7 +297,7 @@ test_expect_success \
git-update-index --add bozbar &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >20.out &&
diff --unified=0 M.out 20.out &&
diff -U0 M.out 20.out &&
check_cache_at bozbar clean &&
sum bozbar frotz nitfol >actual20.sum &&
cmp M.sum actual20.sum'
@ -338,7 +338,7 @@ test_expect_success \
git-update-index --add DF &&
git-read-tree -m -u $treeDF $treeDFDF &&
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'
test_done

View File

@ -47,18 +47,33 @@ void update_tree_entry(struct tree_desc *desc)
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)
{
void *tree = desc->buf;
unsigned long size = desc->size;
int len = strlen(tree)+1;
const unsigned char *sha1 = tree + len;
const char *path = strchr(tree, ' ');
const char *path;
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");
*pathp = path+1;
*pathp = path;
*modep = canon_mode(mode);
return sha1;
}

152
tree.c
View File

@ -3,11 +3,12 @@
#include "blob.h"
#include "commit.h"
#include "tag.h"
#include "tree-walk.h"
#include <stdlib.h>
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;
unsigned int size;
@ -77,19 +78,26 @@ int read_tree_recursive(struct tree *tree,
int stage, const char **match,
read_tree_fn_t fn)
{
struct tree_entry_list *list;
struct tree_desc desc;
if (parse_tree(tree))
return -1;
list = tree->entries;
while (list) {
struct tree_entry_list *current = list;
list = list->next;
if (!match_tree_entry(base, baselen, current->name,
current->mode, match))
desc.buf = tree->buffer;
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 (!match_tree_entry(base, baselen, name, mode, match))
continue;
switch (fn(current->item.any->sha1, base, baselen,
current->name, current->mode, stage)) {
switch (fn(sha1, base, baselen, name, mode, stage)) {
case 0:
continue;
case READ_TREE_RECURSIVE:
@ -97,16 +105,16 @@ int read_tree_recursive(struct tree *tree,
default:
return -1;
}
if (current->directory) {
if (S_ISDIR(mode)) {
int retval;
int pathlen = strlen(current->name);
int pathlen = strlen(name);
char *newbase;
newbase = xmalloc(baselen + 1 + pathlen);
memcpy(newbase, base, baselen);
memcpy(newbase + baselen, current->name, pathlen);
memcpy(newbase + baselen, name, pathlen);
newbase[baselen + pathlen] = '/';
retval = read_tree_recursive(current->item.tree,
retval = read_tree_recursive(lookup_tree(sha1),
newbase,
baselen + pathlen + 1,
stage, match, fn);
@ -143,62 +151,97 @@ struct tree *lookup_tree(const unsigned char *sha1)
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)
{
void *bufptr = buffer;
struct tree_entry_list **list_p;
int n_refs = 0;
if (item->object.parsed)
return 0;
item->object.parsed = 1;
list_p = &item->entries;
while (size) {
struct object *obj;
item->buffer = buffer;
item->size = size;
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;
int len = 1+strlen(bufptr);
unsigned char *file_sha1 = bufptr + len;
char *path = strchr(bufptr, ' ');
unsigned int mode;
if (size < len + 20 || !path ||
sscanf(bufptr, "%o", &mode) != 1)
return -1;
sha1 = tree_entry_extract(&desc, &path, &mode);
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->executable = (mode & S_IXUSR) != 0;
entry->symlink = S_ISLNK(mode) != 0;
entry->zeropad = *(char *)bufptr == '0';
entry->mode = mode;
entry->zeropad = *(const char *)(desc.buf) == '0';
entry->next = NULL;
bufptr += len + 20;
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++;
update_tree_entry(&desc);
*list_p = entry;
list_p = &entry->next;
}
return ret;
}
if (track_object_refs) {
struct tree_entry_list *entry;
unsigned i = 0;
struct object_refs *refs = alloc_object_refs(n_refs);
for (entry = item->entries; entry; entry = entry->next)
refs->ref[i++] = entry->item.any;
set_object_refs(&item->object, refs);
void free_tree_entry_list(struct tree_entry_list *list)
{
while (list) {
struct tree_entry_list *next = list->next;
free(list);
list = next;
}
return 0;
}
int parse_tree(struct tree *item)
@ -206,7 +249,6 @@ int parse_tree(struct tree *item)
char type[20];
void *buffer;
unsigned long size;
int ret;
if (item->object.parsed)
return 0;
@ -219,9 +261,7 @@ int parse_tree(struct tree *item)
return error("Object %s not a tree",
sha1_to_hex(item->object.sha1));
}
ret = parse_tree_buffer(item, buffer, size);
free(buffer);
return ret;
return parse_tree_buffer(item, buffer, size);
}
struct tree *parse_tree_indirect(const unsigned char *sha1)

16
tree.h
View File

@ -12,19 +12,19 @@ struct tree_entry_list {
unsigned symlink : 1;
unsigned zeropad : 1;
unsigned int mode;
char *name;
union {
struct object *any;
struct tree *tree;
struct blob *blob;
} item;
const char *name;
const unsigned char *sha1;
};
struct tree {
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);
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);
#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,
const char *base, int baselen,