Merge branch 'master' into sp/reflog

* master: (90 commits)
  fetch.c: remove an unused variable and dead code.
  Clean up sha1 file writing
  Builtin git-cat-file
  builtin format-patch: squelch content-type for 7-bit ASCII
  CMIT_FMT_EMAIL: Q-encode Subject: and display-name part of From: fields.
  add more informative error messages to git-mktag
  remove the artificial restriction tagsize < 8kb
  git-rebase: use canonical A..B syntax to format-patch
  git-format-patch: now built-in.
  fmt-patch: Support --attach
  fmt-patch: understand old <his> notation
  Teach fmt-patch about --keep-subject
  Teach fmt-patch about --numbered
  fmt-patch: implement -o <dir>
  fmt-patch: output file names to stdout
  Teach fmt-patch to write individual files.
  built-in tar-tree and remote tar-tree
  Builtin git-diff-files, git-diff-index, git-diff-stages, and git-diff-tree.
  Builtin git-show-branch.
  Builtin git-apply.
  ...
This commit is contained in:
Junio C Hamano 2006-05-24 16:49:24 -07:00
Родитель 6858d49492 84c667ff97
Коммит a5c8a98ca7
76 изменённых файлов: 3466 добавлений и 1592 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -77,6 +77,7 @@ git-prune
git-prune-packed
git-pull
git-push
git-quiltimport
git-read-tree
git-rebase
git-receive-pack

Просмотреть файл

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

Просмотреть файл

@ -266,8 +266,8 @@ This recipe appears to work with the current [*1*] Thunderbird from Suse.
The following Thunderbird extensions are needed:
AboutConfig 0.5
http://aboutconfig.mozdev.org/
External Editor 0.5.4
http://extensionroom.mozdev.org/more-info/exteditor
External Editor 0.7.2
http://globs.org/articles.php?lng=en&pg=8
1) Prepare the patch as a text file using your method of choice.

Просмотреть файл

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

Просмотреть файл

@ -49,6 +49,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

Просмотреть файл

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

Просмотреть файл

@ -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.
-l::
Create the new branch's ref log. This activates recording of

Просмотреть файл

@ -0,0 +1,61 @@
git-quiltimport(1)
================
NAME
----
git-quiltimport - Applies a quilt patchset onto the current branch
SYNOPSIS
--------
[verse]
'git-quiltimport' [--dry-run] [--author <author>] [--patches <dir>]
DESCRIPTION
-----------
Applies a quilt patchset onto the current git branch, preserving
the patch boundaries, patch order, and patch descriptions present
in the quilt patchset.
For each patch the code attempts to extract the author from the
patch description. If that fails it falls back to the author
specified with --author. If the --author flag was not given
the patch description is displayed and the user is asked to
interactively enter the author of the patch.
If a subject is not found in the patch description the patch name is
preserved as the 1 line subject in the git description.
OPTIONS
-------
--dry-run::
Walk through the patches in the series and warn
if we cannot find all of the necessary information to commit
a patch. At the time of this writing only missing author
information is warned about.
--author Author Name <Author Email>::
The author name and email address to use when no author
information can be found in the patch description.
--patches <dir>::
The directory to find the quilt patches and the
quilt series file.
The default for the patch directory is patches
or the value of the $QUILT_PATCHES environment
variable.
Author
------
Written by Eric Biederman <ebiederm@lnxi.com>
Documentation
--------------
Documentation by Eric Biederman <ebiederm@lnxi.com>
GIT
---
Part of the gitlink:git[7] suite

Просмотреть файл

@ -4,12 +4,16 @@ T="$1"
for h in *.html *.txt howto/*.txt howto/*.html
do
diff -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h" || {
if test -f "$T/$h" &&
diff -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h"
then
:; # up to date
else
echo >&2 "# install $h $T/$h"
rm -f "$T/$h"
mkdir -p `dirname "$T/$h"`
cp "$h" "$T/$h"
}
fi
done
strip_leading=`echo "$T/" | sed -e 's|.|.|g'`
for th in "$T"/*.html "$T"/*.txt "$T"/howto/*.txt "$T"/howto/*.html

Просмотреть файл

@ -0,0 +1,391 @@
A tutorial introduction to git: part two
========================================
You should work through link:tutorial.html[A tutorial introduction to
git] before reading this tutorial.
The goal of this tutorial is to introduce two fundamental pieces of
git's architecture--the object database and the index file--and to
provide the reader with everything necessary to understand the rest
of the git documentation.
The git object database
-----------------------
Let's start a new project and create a small amount of history:
------------------------------------------------
$ mkdir test-project
$ cd test-project
$ git init-db
defaulting to local storage area
$ echo 'hello world' > file.txt
$ git add .
$ git commit -a -m "initial commit"
Committing initial tree 92b8b694ffb1675e5975148e1121810081dbdffe
$ echo 'hello world!' >file.txt
$ git commit -a -m "add emphasis"
------------------------------------------------
What are the 40 digits of hex that git responded to the first commit
with?
We saw in part one of the tutorial that commits have names like this.
It turns out that every object in the git history is stored under
such a 40-digit hex name. That name is the SHA1 hash of the object's
contents; among other things, this ensures that git will never store
the same data twice (since identical data is given an identical SHA1
name), and that the contents of a git object will never change (since
that would change the object's name as well).
We can ask git about this particular object with the cat-file
command--just cut-and-paste from the reply to the initial commit, to
save yourself typing all 40 hex digits:
------------------------------------------------
$ git cat-file -t 92b8b694ffb1675e5975148e1121810081dbdffe
tree
------------------------------------------------
A tree can refer to one or more "blob" objects, each corresponding to
a file. In addition, a tree can also refer to other tree objects,
thus creating a directory heirarchy. You can examine the contents of
any tree using ls-tree (remember that a long enough initial portion
of the SHA1 will also work):
------------------------------------------------
$ git ls-tree 92b8b694
100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad file.txt
------------------------------------------------
Thus we see that this tree has one file in it. The SHA1 hash is a
reference to that file's data:
------------------------------------------------
$ git cat-file -t 3b18e512
blob
------------------------------------------------
A "blob" is just file data, which we can also examine with cat-file:
------------------------------------------------
$ git cat-file blob 3b18e512
hello world
------------------------------------------------
Note that this is the old file data; so the object that git named in
its response to the initial tree was a tree with a snapshot of the
directory state that was recorded by the first commit.
All of these objects are stored under their SHA1 names inside the git
directory:
------------------------------------------------
$ find .git/objects/
.git/objects/
.git/objects/pack
.git/objects/info
.git/objects/3b
.git/objects/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad
.git/objects/92
.git/objects/92/b8b694ffb1675e5975148e1121810081dbdffe
.git/objects/54
.git/objects/54/196cc2703dc165cbd373a65a4dcf22d50ae7f7
.git/objects/a0
.git/objects/a0/423896973644771497bdc03eb99d5281615b51
.git/objects/d0
.git/objects/d0/492b368b66bdabf2ac1fd8c92b39d3db916e59
.git/objects/c4
.git/objects/c4/d59f390b9cfd4318117afde11d601c1085f241
------------------------------------------------
and the contents of these files is just the compressed data plus a
header identifying their length and their type. The type is either a
blob, a tree, a commit, or a tag. We've seen a blob and a tree now,
so next we should look at a commit.
The simplest commit to find is the HEAD commit, which we can find
from .git/HEAD:
------------------------------------------------
$ cat .git/HEAD
ref: refs/heads/master
------------------------------------------------
As you can see, this tells us which branch we're currently on, and it
tells us this by naming a file under the .git directory, which itself
contains a SHA1 name referring to a commit object, which we can
examine with cat-file:
------------------------------------------------
$ cat .git/refs/heads/master
c4d59f390b9cfd4318117afde11d601c1085f241
$ git cat-file -t c4d59f39
commit
$ git cat-file commit c4d59f39
tree d0492b368b66bdabf2ac1fd8c92b39d3db916e59
parent 54196cc2703dc165cbd373a65a4dcf22d50ae7f7
author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143418702 -0500
committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143418702 -0500
add emphasis
------------------------------------------------
The "tree" object here refers to the new state of the tree:
------------------------------------------------
$ git ls-tree d0492b36
100644 blob a0423896973644771497bdc03eb99d5281615b51 file.txt
$ git cat-file commit a0423896
hello world!
------------------------------------------------
and the "parent" object refers to the previous commit:
------------------------------------------------
$ git-cat-file commit 54196cc2
tree 92b8b694ffb1675e5975148e1121810081dbdffe
author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500
committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500
initial commit
------------------------------------------------
The tree object is the tree we examined first, and this commit is
unusual in that it lacks any parent.
Most commits have only one parent, but it is also common for a commit
to have multiple parents. In that case the commit represents a
merge, with the parent references pointing to the heads of the merged
branches.
Besides blobs, trees, and commits, the only remaining type of object
is a "tag", which we won't discuss here; refer to gitlink:git-tag[1]
for details.
So now we know how git uses the object database to represent a
project's history:
* "commit" objects refer to "tree" objects representing the
snapshot of a directory tree at a particular point in the
history, and refer to "parent" commits to show how they're
connected into the project history.
* "tree" objects represent the state of a single directory,
associating directory names to "blob" objects containing file
data and "tree" objects containing subdirectory information.
* "blob" objects contain file data without any other structure.
* References to commit objects at the head of each branch are
stored in files under .git/refs/heads/.
* The name of the current branch is stored in .git/HEAD.
Note, by the way, that lots of commands take a tree as an argument.
But as we can see above, a tree can be referred to in many different
ways--by the SHA1 name for that tree, by the name of a commit that
refers to the tree, by the name of a branch whose head refers to that
tree, etc.--and most such commands can accept any of these names.
In command synopses, the word "tree-ish" is sometimes used to
designate such an argument.
The index file
--------------
The primary tool we've been using to create commits is "git commit
-a", which creates a commit including every change you've made to
your working tree. But what if you want to commit changes only to
certain files? Or only certain changes to certain files?
If we look at the way commits are created under the cover, we'll see
that there are more flexible ways creating commits.
Continuing with our test-project, let's modify file.txt again:
------------------------------------------------
$ echo "hello world, again" >>file.txt
------------------------------------------------
but this time instead of immediately making the commit, let's take an
intermediate step, and ask for diffs along the way to keep track of
what's happening:
------------------------------------------------
$ git diff
--- a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
hello world!
+hello world, again
$ git update-index file.txt
$ git diff
------------------------------------------------
The last diff is empty, but no new commits have been made, and the
head still doesn't contain the new line:
------------------------------------------------
$ git-diff HEAD
diff --git a/file.txt b/file.txt
index a042389..513feba 100644
--- a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
hello world!
+hello world, again
------------------------------------------------
So "git diff" is comparing against something other than the head.
The thing that it's comparing against is actually the index file,
which is stored in .git/index in a binary format, but whose contents
we can examine with ls-files:
------------------------------------------------
$ git ls-files --stage
100644 513feba2e53ebbd2532419ded848ba19de88ba00 0 file.txt
$ git cat-file -t 513feba2
blob
$ git cat-file blob 513feba2
hello world, again
------------------------------------------------
So what our "git update-index" did was store a new blob and then put
a reference to it in the index file. If we modify the file again,
we'll see that the new modifications are reflected in the "git-diff"
output:
------------------------------------------------
$ echo 'again?' >>file.txt
$ git diff
index 513feba..ba3da7b 100644
--- a/file.txt
+++ b/file.txt
@@ -1,2 +1,3 @@
hello world!
hello world, again
+again?
------------------------------------------------
With the right arguments, git diff can also show us the difference
between the working directory and the last commit, or between the
index and the last commit:
------------------------------------------------
$ git diff HEAD
diff --git a/file.txt b/file.txt
index a042389..ba3da7b 100644
--- a/file.txt
+++ b/file.txt
@@ -1 +1,3 @@
hello world!
+hello world, again
+again?
$ git diff --cached
diff --git a/file.txt b/file.txt
index a042389..513feba 100644
--- a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
hello world!
+hello world, again
------------------------------------------------
At any time, we can create a new commit using "git commit" (without
the -a option), and verify that the state committed only includes the
changes stored in the index file, not the additional change that is
still only in our working tree:
------------------------------------------------
$ git commit -m "repeat"
$ git diff HEAD
diff --git a/file.txt b/file.txt
index 513feba..ba3da7b 100644
--- a/file.txt
+++ b/file.txt
@@ -1,2 +1,3 @@
hello world!
hello world, again
+again?
------------------------------------------------
So by default "git commit" uses the index to create the commit, not
the working tree; the -a option to commit tells it to first update
the index with all changes in the working tree.
Finally, it's worth looking at the effect of "git add" on the index
file:
------------------------------------------------
$ echo "goodbye, world" >closing.txt
$ git add closing.txt
------------------------------------------------
The effect of the "git add" was to add one entry to the index file:
------------------------------------------------
$ git ls-files --stage
100644 8b9743b20d4b15be3955fc8d5cd2b09cd2336138 0 closing.txt
100644 513feba2e53ebbd2532419ded848ba19de88ba00 0 file.txt
------------------------------------------------
And, as you can see with cat-file, this new entry refers to the
current contents of the file:
------------------------------------------------
$ git cat-file blob a6b11f7a
goodbye, word
------------------------------------------------
The "status" command is a useful way to get a quick summary of the
situation:
------------------------------------------------
$ git status
#
# Updated but not checked in:
# (will commit)
#
# new file: closing.txt
#
#
# Changed but not updated:
# (use git-update-index to mark for commit)
#
# modified: file.txt
#
------------------------------------------------
Since the current state of closing.txt is cached in the index file,
it is listed as "updated but not checked in". Since file.txt has
changes in the working directory that aren't reflected in the index,
it is marked "changed but not updated". At this point, running "git
commit" would create a commit that added closing.txt (with its new
contents), but that didn't modify file.txt.
Also, note that a bare "git diff" shows the changes to file.txt, but
not the addition of closing.txt, because the version of closing.txt
in the index file is identical to the one in the working directory.
In addition to being the staging area for new commits, the index file
is also populated from the object database when checking out a
branch, and is used to hold the trees involved in a merge operation.
See the link:core-tutorial.txt[core tutorial] and the relevant man
pages for details.
What next?
----------
At this point you should know everything necessary to read the man
pages for any of the git commands; one good place to start would be
with the commands mentioned in link:everyday.html[Everyday git]. You
should be able to find any unknown jargon in the
link:glossary.html[Glosssay].
The link:cvs-migration.html[CVS migration] document explains how to
import a CVS repository into git, and shows how to use git in a
CVS-like way.
For some interesting examples of git use, see the
link:howto-index.html[howtos].
For git developers, the link:core-tutorial.html[Core tutorial] goes
into detail on the lower-level git mechanisms involved in, for
example, creating a new commit.

Просмотреть файл

@ -80,13 +80,13 @@ file; just remove it, then commit.
At any point you can view the history of your changes using
------------------------------------------------
$ 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].

Просмотреть файл

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

Просмотреть файл

@ -113,19 +113,19 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
### --- END CONFIGURATION SECTION ---
SCRIPT_SH = \
git-add.sh git-bisect.sh git-branch.sh git-checkout.sh \
git-bisect.sh git-branch.sh git-checkout.sh \
git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
git-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 \
git-resolve.sh git-revert.sh git-rm.sh git-sh-setup.sh \
git-resolve.sh git-revert.sh git-sh-setup.sh \
git-tag.sh git-verify-tag.sh \
git-applymbox.sh git-applypatch.sh git-am.sh \
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
git-merge-resolve.sh git-merge-ours.sh \
git-lost-found.sh
git-lost-found.sh git-quiltimport.sh
SCRIPT_PERL = \
git-archimport.perl git-cvsimport.perl git-relink.perl \
@ -149,28 +149,31 @@ 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-hash-object$X git-index-pack$X git-init-db$X git-local-fetch$X \
git-ls-files$X git-ls-tree$X git-mailinfo$X git-merge-base$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-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-receive-pack$X git-rev-list$X git-rev-parse$X \
git-send-pack$X git-show-branch$X git-shell$X \
git-peek-remote$X git-prune-packed$X \
git-receive-pack$X git-rev-parse$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 git-check-ref-format$X \
git-update-ref$X git-symbolic-ref$X \
git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
git-describe$X git-merge-tree$X git-blame$X git-imap-send$X
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-grep$X git-add$X git-rm$X git-rev-list$X \
git-check-ref-format$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)
@ -199,7 +202,7 @@ LIB_H = \
blob.h cache.h commit.h csum-file.h delta.h \
diff.h object.h pack.h pkt-line.h quote.h refs.h \
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
tree-walk.h log-tree.h
tree-walk.h log-tree.h dir.h
DIFF_OBJS = \
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@ -210,7 +213,7 @@ LIB_OBJS = \
blob.o commit.o connect.o csum-file.o base85.o \
date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \
object.o pack-check.o patch-delta.o path.o pkt-line.o \
quote.o read-cache.o refs.o run-command.o \
quote.o read-cache.o refs.o run-command.o dir.o \
server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
@ -218,7 +221,14 @@ LIB_OBJS = \
BUILTIN_OBJS = \
builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
builtin-grep.o
builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.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
@ -418,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
@ -459,6 +472,7 @@ PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
GIT_PYTHON_DIR_SQ = $(subst ','\'',$(GIT_PYTHON_DIR))
ALL_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' $(COMPAT_CFLAGS)
ALL_CFLAGS += -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"'
LIB_OBJS += $(COMPAT_OBJS)
export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
### Build rules
@ -565,10 +579,6 @@ git-http-push$X: revision.o http.o http-push.o $(LIB_FILE)
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
init-db.o: init-db.c
$(CC) -c $(ALL_CFLAGS) \
-DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $*.c
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
$(patsubst git-%$X,%.o,$(PROGRAMS)): $(GITLIBS)
$(DIFF_OBJS): diffcore.h
@ -625,7 +635,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
@ -652,6 +669,25 @@ dist: git.spec git-tar-tree
rpm: dist
$(RPMBUILD) -ta $(GIT_TARNAME).tar.gz
htmldocs = git-htmldocs-$(GIT_VERSION)
manpages = git-manpages-$(GIT_VERSION)
dist-doc:
rm -fr .doc-tmp-dir
mkdir .doc-tmp-dir
$(MAKE) -C Documentation WEBDOC_DEST=../.doc-tmp-dir install-webdoc
cd .doc-tmp-dir && $(TAR) cf ../$(htmldocs).tar .
gzip -n -9 -f $(htmldocs).tar
:
rm -fr .doc-tmp-dir
mkdir .doc-tmp-dir .doc-tmp-dir/man1 .doc-tmp-dir/man7
$(MAKE) -C Documentation DESTDIR=. \
man1=../.doc-tmp-dir/man1 \
man7=../.doc-tmp-dir/man7 \
install
cd .doc-tmp-dir && $(TAR) cf ../$(manpages).tar .
gzip -n -9 -f $(manpages).tar
rm -fr .doc-tmp-dir
### Cleaning rules
clean:
@ -659,8 +695,9 @@ clean:
$(LIB_FILE) $(XDIFF_LIB)
rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X
rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
rm -rf $(GIT_TARNAME)
rm -rf $(GIT_TARNAME) .doc-tmp-dir
rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
rm -f $(htmldocs).tar $(manpages).tar
$(MAKE) -C Documentation/ clean
$(MAKE) -C templates clean
$(MAKE) -C t/ clean

187
builtin-add.c Normal file
Просмотреть файл

@ -0,0 +1,187 @@
/*
* "git add" builtin command
*
* Copyright (C) 2006 Linus Torvalds
*/
#include <fnmatch.h>
#include "cache.h"
#include "builtin.h"
#include "dir.h"
static const char builtin_add_usage[] =
"git-add [-n] [-v] <filepattern>...";
static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
{
char *seen;
int i, specs;
struct dir_entry **src, **dst;
for (specs = 0; pathspec[specs]; specs++)
/* nothing */;
seen = xmalloc(specs);
memset(seen, 0, specs);
src = dst = dir->entries;
i = dir->nr;
while (--i >= 0) {
struct dir_entry *entry = *src++;
if (!match_pathspec(pathspec, entry->name, entry->len, prefix, seen)) {
free(entry);
continue;
}
*dst++ = entry;
}
dir->nr = dst - dir->entries;
for (i = 0; i < specs; i++) {
struct stat st;
const char *match;
if (seen[i])
continue;
/* Existing file? We must have ignored it */
match = pathspec[i];
if (!match[0] || !lstat(match, &st))
continue;
die("pathspec '%s' did not match any files", match);
}
}
static void fill_directory(struct dir_struct *dir, const char **pathspec)
{
const char *path, *base;
int baselen;
/* Set up the default git porcelain excludes */
memset(dir, 0, sizeof(*dir));
dir->exclude_per_dir = ".gitignore";
path = git_path("info/exclude");
if (!access(path, R_OK))
add_excludes_from_file(dir, path);
/*
* Calculate common prefix for the pathspec, and
* use that to optimize the directory walk
*/
baselen = common_prefix(pathspec);
path = ".";
base = "";
if (baselen) {
char *common = xmalloc(baselen + 1);
common = xmalloc(baselen + 1);
memcpy(common, *pathspec, baselen);
common[baselen] = 0;
path = base = common;
}
/* Read the directory and prune it */
read_directory(dir, path, base, baselen);
if (pathspec)
prune_directory(dir, pathspec, baselen);
}
static int add_file_to_index(const char *path, int verbose)
{
int size, namelen;
struct stat st;
struct cache_entry *ce;
if (lstat(path, &st))
die("%s: unable to stat (%s)", path, strerror(errno));
if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
die("%s: can only add regular files or symbolic links", path);
namelen = strlen(path);
size = cache_entry_size(namelen);
ce = xcalloc(1, size);
memcpy(ce->name, path, namelen);
ce->ce_flags = htons(namelen);
fill_stat_cache_info(ce, &st);
ce->ce_mode = create_ce_mode(st.st_mode);
if (!trust_executable_bit) {
/* If there is an existing entry, pick the mode bits
* from it.
*/
int pos = cache_name_pos(path, namelen);
if (pos >= 0)
ce->ce_mode = active_cache[pos]->ce_mode;
}
if (index_path(ce->sha1, path, &st, 1))
die("unable to index file %s", path);
if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD))
die("unable to add %s to index",path);
if (verbose)
printf("add '%s'\n", path);
return 0;
}
static struct cache_file cache_file;
int cmd_add(int argc, const char **argv, char **envp)
{
int i, newfd;
int verbose = 0, show_only = 0;
const char *prefix = setup_git_directory();
const char **pathspec;
struct dir_struct dir;
git_config(git_default_config);
newfd = hold_index_file_for_update(&cache_file, get_index_file());
if (newfd < 0)
die("unable to create new cachefile");
if (read_cache() < 0)
die("index file corrupt");
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (arg[0] != '-')
break;
if (!strcmp(arg, "--")) {
i++;
break;
}
if (!strcmp(arg, "-n")) {
show_only = 1;
continue;
}
if (!strcmp(arg, "-v")) {
verbose = 1;
continue;
}
die(builtin_add_usage);
}
git_config(git_default_config);
pathspec = get_pathspec(prefix, argv + i);
fill_directory(&dir, pathspec);
if (show_only) {
const char *sep = "", *eof = "";
for (i = 0; i < dir.nr; i++) {
printf("%s%s", sep, dir.entries[i]->name);
sep = " ";
eof = "\n";
}
fputs(eof, stdout);
return 0;
}
for (i = 0; i < dir.nr; i++)
add_file_to_index(dir.entries[i]->name, verbose);
if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
commit_index_file(&cache_file))
die("Unable to write new index file");
}
return 0;
}

Просмотреть файл

@ -11,12 +11,15 @@
#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
// --stat does just a diffstat, and doesn't actually apply
// --numstat does numeric diffstat, and doesn't actually apply
// --index-info shows the old and new index info for paths if available.
// --index updates the cache as well.
// --cached updates only the cache without ever touching the working tree.
//
static const char *prefix;
static int prefix_length = -1;
@ -26,6 +29,7 @@ static int p_value = 1;
static int allow_binary_replacement = 0;
static int check_index = 0;
static int write_index = 0;
static int cached = 0;
static int diffstat = 0;
static int numstat = 0;
static int summary = 0;
@ -36,7 +40,7 @@ static int show_index_info = 0;
static int line_termination = '\n';
static unsigned long p_context = -1;
static const char apply_usage[] =
"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
static enum whitespace_eol {
nowarn_whitespace,
@ -1600,7 +1604,7 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
return 0;
}
static int apply_data(struct patch *patch, struct stat *st)
static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
{
char *buf;
unsigned long size, alloc;
@ -1609,7 +1613,17 @@ static int apply_data(struct patch *patch, struct stat *st)
size = 0;
alloc = 0;
buf = NULL;
if (patch->old_name) {
if (cached) {
if (ce) {
char type[20];
buf = read_sha1_file(ce->sha1, type, &size);
if (!buf)
return error("read of %s failed",
patch->old_name);
alloc = size;
}
}
else if (patch->old_name) {
size = st->st_size;
alloc = size + 8192;
buf = xmalloc(alloc);
@ -1637,16 +1651,21 @@ static int check_patch(struct patch *patch)
const char *old_name = patch->old_name;
const char *new_name = patch->new_name;
const char *name = old_name ? old_name : new_name;
struct cache_entry *ce = NULL;
if (old_name) {
int changed;
int stat_ret = lstat(old_name, &st);
int changed = 0;
int stat_ret = 0;
unsigned st_mode = 0;
if (!cached)
stat_ret = lstat(old_name, &st);
if (check_index) {
int pos = cache_name_pos(old_name, strlen(old_name));
if (pos < 0)
return error("%s: does not exist in index",
old_name);
ce = active_cache[pos];
if (stat_ret < 0) {
struct checkout costate;
if (errno != ENOENT)
@ -1659,40 +1678,46 @@ static int check_patch(struct patch *patch)
costate.quiet = 0;
costate.not_new = 0;
costate.refresh_cache = 1;
if (checkout_entry(active_cache[pos],
if (checkout_entry(ce,
&costate,
NULL) ||
lstat(old_name, &st))
return -1;
}
changed = ce_match_stat(active_cache[pos], &st, 1);
if (!cached)
changed = ce_match_stat(ce, &st, 1);
if (changed)
return error("%s: does not match index",
old_name);
if (cached)
st_mode = ntohl(ce->ce_mode);
}
else if (stat_ret < 0)
return error("%s: %s", old_name, strerror(errno));
if (!cached)
st_mode = ntohl(create_ce_mode(st.st_mode));
if (patch->is_new < 0)
patch->is_new = 0;
st.st_mode = ntohl(create_ce_mode(st.st_mode));
if (!patch->old_mode)
patch->old_mode = st.st_mode;
if ((st.st_mode ^ patch->old_mode) & S_IFMT)
patch->old_mode = st_mode;
if ((st_mode ^ patch->old_mode) & S_IFMT)
return error("%s: wrong type", old_name);
if (st.st_mode != patch->old_mode)
if (st_mode != patch->old_mode)
fprintf(stderr, "warning: %s has type %o, expected %o\n",
old_name, st.st_mode, patch->old_mode);
old_name, st_mode, patch->old_mode);
}
if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) {
if (check_index && cache_name_pos(new_name, strlen(new_name)) >= 0)
return error("%s: already exists in index", new_name);
if (!lstat(new_name, &st))
return error("%s: already exists in working directory", new_name);
if (errno != ENOENT)
return error("%s: %s", new_name, strerror(errno));
if (!cached) {
if (!lstat(new_name, &st))
return error("%s: already exists in working directory", new_name);
if (errno != ENOENT)
return error("%s: %s", new_name, strerror(errno));
}
if (!patch->new_mode) {
if (patch->is_new)
patch->new_mode = S_IFREG | 0644;
@ -1709,9 +1734,9 @@ static int check_patch(struct patch *patch)
return error("new mode (%o) of %s does not match old mode (%o)%s%s",
patch->new_mode, new_name, patch->old_mode,
same ? "" : " of ", same ? "" : old_name);
}
}
if (apply_data(patch, &st) < 0)
if (apply_data(patch, &st, ce) < 0)
return error("%s: patch does not apply", name);
return 0;
}
@ -1778,7 +1803,7 @@ static void numstat_patch_list(struct patch *patch)
{
for ( ; patch; patch = patch->next) {
const char *name;
name = patch->old_name ? patch->old_name : patch->new_name;
name = patch->new_name ? patch->new_name : patch->old_name;
printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
if (line_termination && quote_c_style(name, NULL, NULL, 0))
quote_c_style(name, NULL, stdout, 0);
@ -1894,7 +1919,8 @@ static void remove_file(struct patch *patch)
if (remove_file_from_cache(patch->old_name) < 0)
die("unable to remove %s from index", patch->old_name);
}
unlink(patch->old_name);
if (!cached)
unlink(patch->old_name);
}
static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size)
@ -1911,9 +1937,11 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
memcpy(ce->name, path, namelen);
ce->ce_mode = create_ce_mode(mode);
ce->ce_flags = htons(namelen);
if (lstat(path, &st) < 0)
die("unable to stat newly created file %s", path);
fill_stat_cache_info(ce, &st);
if (!cached) {
if (lstat(path, &st) < 0)
die("unable to stat newly created file %s", path);
fill_stat_cache_info(ce, &st);
}
if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
die("unable to create backing store for newly created file %s", path);
if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
@ -1950,6 +1978,8 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
*/
static void create_one_file(char *path, unsigned mode, const char *buf, unsigned long size)
{
if (cached)
return;
if (!try_create_file(path, mode, buf, size))
return;
@ -2122,7 +2152,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;
@ -2182,6 +2212,11 @@ int main(int argc, char **argv)
check_index = 1;
continue;
}
if (!strcmp(arg, "--cached")) {
check_index = 1;
cached = 1;
continue;
}
if (!strcmp(arg, "--apply")) {
apply = 1;
continue;

Просмотреть файл

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

Просмотреть файл

@ -0,0 +1,14 @@
/*
* GIT - The information manager from hell
*/
#include "cache.h"
#include "refs.h"
#include "builtin.h"
int cmd_check_ref_format(int argc, const char **argv, char **envp)
{
if (argc != 2)
usage("git check-ref-format refname");
return !!check_ref_format(argv[1]);
}

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -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];
@ -138,7 +139,10 @@ int main(int argc, const char **argv)
opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
DIFF_SETUP_USE_CACHE);
while (fgets(line, sizeof(line), stdin))
diff_tree_stdin(line);
if (line[0] == '\n')
fflush(stdout);
else
diff_tree_stdin(line);
return 0;
}

Просмотреть файл

@ -122,7 +122,7 @@ static int builtin_diff_b_f(struct rev_info *revs,
stuff_change(&revs->diffopt,
canon_mode(st.st_mode), canon_mode(st.st_mode),
blob[0].sha1, null_sha1,
blob[0].name, path);
path, path);
diffcore_std(&revs->diffopt);
diff_flush(&revs->diffopt);
return 0;
@ -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;

Просмотреть файл

@ -453,7 +453,6 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
len = nr = 0;
push_arg("grep");
push_arg("-H");
if (opt->fixed)
push_arg("-F");
if (opt->linenum)
@ -503,17 +502,35 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
push_arg("-e");
push_arg(p->pattern);
}
push_arg("--");
/*
* To make sure we get the header printed out when we want it,
* add /dev/null to the paths to grep. This is unnecessary
* (and wrong) with "-l" or "-L", which always print out the
* name anyway.
*
* GNU grep has "-H", but this is portable.
*/
if (!opt->name_only && !opt->unmatch_name_only)
push_arg("/dev/null");
hit = 0;
argc = nr;
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
char *name;
if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode)))
continue;
if (!pathspec_matches(paths, ce->name))
continue;
argv[argc++] = ce->name;
name = ce->name;
if (name[0] == '-') {
int len = ce_namelen(ce);
name = xmalloc(len + 3);
memcpy(name, "./", 2);
memcpy(name + 2, ce->name, len + 1);
}
argv[argc++] = name;
if (argc < MAXARGS)
continue;
hit += exec_grep(argc, argv);

Просмотреть файл

@ -4,6 +4,7 @@
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
#include "builtin.h"
#ifndef DEFAULT_GIT_TEMPLATE_DIR
#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates/"
@ -116,7 +117,7 @@ static void copy_templates_1(char *path, int baselen,
}
}
static void copy_templates(const char *git_dir, int len, char *template_dir)
static void copy_templates(const char *git_dir, int len, const char *template_dir)
{
char path[PATH_MAX];
char template_path[PATH_MAX];
@ -163,7 +164,7 @@ static void copy_templates(const char *git_dir, int len, char *template_dir)
closedir(dir);
}
static void create_default_files(const char *git_dir, char *template_path)
static void create_default_files(const char *git_dir, const char *template_path)
{
unsigned len = strlen(git_dir);
static char path[PATH_MAX];
@ -234,15 +235,16 @@ static const char init_db_usage[] =
* On the other hand, it might just make lookup slower and messier. You
* be the judge. The default case is to have one DB per managed directory.
*/
int main(int argc, char **argv)
int cmd_init_db(int argc, const char **argv, char **envp)
{
const char *git_dir;
const char *sha1_dir;
char *path, *template_dir = NULL;
const char *template_dir = NULL;
char *path;
int len, i;
for (i = 1; i < argc; i++, argv++) {
char *arg = argv[1];
const char *arg = argv[1];
if (!strncmp(arg, "--template=", 11))
template_dir = arg+11;
else if (!strcmp(arg, "--shared"))

Просмотреть файл

@ -11,6 +11,9 @@
#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)
{
@ -20,6 +23,13 @@ static int cmd_log_wc(int argc, const char **argv, char **envp,
rev->commit_format = CMIT_FMT_DEFAULT;
rev->verbose_header = 1;
argc = setup_revisions(argc, argv, rev, "HEAD");
if (rev->always_show_header) {
if (rev->diffopt.pickaxe || rev->diffopt.filter) {
rev->always_show_header = 0;
if (rev->diffopt.output_format == DIFF_FORMAT_RAW)
rev->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;
}
}
if (argc > 1)
die("unrecognized argument: %s", argv[1]);
@ -69,12 +79,76 @@ int cmd_log(int argc, const char **argv, char **envp)
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;
int nr = 0, total, i, j;
int use_stdout = 0;
int numbered = 0;
int keep_subject = 0;
init_revisions(&rev);
rev.commit_format = CMIT_FMT_EMAIL;
@ -85,23 +159,89 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
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 (!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 (numbered && keep_subject < 0)
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;
while (0 <= --nr) {
int shown;
commit = list[nr];
rev.nr = total - nr;
if (!use_stdout)
reopen_stdout(commit, rev.nr, keep_subject);
shown = log_tree_commit(&rev, commit);
free(commit->buffer);
commit->buffer = NULL;
if (shown)
printf("-- \n%s\n\n", git_version_string);
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;
}

Просмотреть файл

@ -5,23 +5,21 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
#include <dirent.h>
#include <fnmatch.h>
#include "cache.h"
#include "quote.h"
#include "dir.h"
#include "builtin.h"
static int abbrev = 0;
static int show_deleted = 0;
static int show_cached = 0;
static int show_others = 0;
static int show_ignored = 0;
static int show_stage = 0;
static int show_unmerged = 0;
static int show_modified = 0;
static int show_killed = 0;
static int show_other_directories = 0;
static int hide_empty_directories = 0;
static int show_valid_bit = 0;
static int line_terminator = '\n';
@ -38,309 +36,6 @@ static const char *tag_other = "";
static const char *tag_killed = "";
static const char *tag_modified = "";
static const char *exclude_per_dir = NULL;
/* We maintain three exclude pattern lists:
* EXC_CMDL lists patterns explicitly given on the command line.
* EXC_DIRS lists patterns obtained from per-directory ignore files.
* EXC_FILE lists patterns from fallback ignore files.
*/
#define EXC_CMDL 0
#define EXC_DIRS 1
#define EXC_FILE 2
static struct exclude_list {
int nr;
int alloc;
struct exclude {
const char *pattern;
const char *base;
int baselen;
} **excludes;
} exclude_list[3];
static void add_exclude(const char *string, const char *base,
int baselen, struct exclude_list *which)
{
struct exclude *x = xmalloc(sizeof (*x));
x->pattern = string;
x->base = base;
x->baselen = baselen;
if (which->nr == which->alloc) {
which->alloc = alloc_nr(which->alloc);
which->excludes = realloc(which->excludes,
which->alloc * sizeof(x));
}
which->excludes[which->nr++] = x;
}
static int add_excludes_from_file_1(const char *fname,
const char *base,
int baselen,
struct exclude_list *which)
{
int fd, i;
long size;
char *buf, *entry;
fd = open(fname, O_RDONLY);
if (fd < 0)
goto err;
size = lseek(fd, 0, SEEK_END);
if (size < 0)
goto err;
lseek(fd, 0, SEEK_SET);
if (size == 0) {
close(fd);
return 0;
}
buf = xmalloc(size+1);
if (read(fd, buf, size) != size)
goto err;
close(fd);
buf[size++] = '\n';
entry = buf;
for (i = 0; i < size; i++) {
if (buf[i] == '\n') {
if (entry != buf + i && entry[0] != '#') {
buf[i - (i && buf[i-1] == '\r')] = 0;
add_exclude(entry, base, baselen, which);
}
entry = buf + i + 1;
}
}
return 0;
err:
if (0 <= fd)
close(fd);
return -1;
}
static void add_excludes_from_file(const char *fname)
{
if (add_excludes_from_file_1(fname, "", 0,
&exclude_list[EXC_FILE]) < 0)
die("cannot use %s as an exclude file", fname);
}
static int push_exclude_per_directory(const char *base, int baselen)
{
char exclude_file[PATH_MAX];
struct exclude_list *el = &exclude_list[EXC_DIRS];
int current_nr = el->nr;
if (exclude_per_dir) {
memcpy(exclude_file, base, baselen);
strcpy(exclude_file + baselen, exclude_per_dir);
add_excludes_from_file_1(exclude_file, base, baselen, el);
}
return current_nr;
}
static void pop_exclude_per_directory(int stk)
{
struct exclude_list *el = &exclude_list[EXC_DIRS];
while (stk < el->nr)
free(el->excludes[--el->nr]);
}
/* Scan the list and let the last match determines the fate.
* Return 1 for exclude, 0 for include and -1 for undecided.
*/
static int excluded_1(const char *pathname,
int pathlen,
struct exclude_list *el)
{
int i;
if (el->nr) {
for (i = el->nr - 1; 0 <= i; i--) {
struct exclude *x = el->excludes[i];
const char *exclude = x->pattern;
int to_exclude = 1;
if (*exclude == '!') {
to_exclude = 0;
exclude++;
}
if (!strchr(exclude, '/')) {
/* match basename */
const char *basename = strrchr(pathname, '/');
basename = (basename) ? basename+1 : pathname;
if (fnmatch(exclude, basename, 0) == 0)
return to_exclude;
}
else {
/* match with FNM_PATHNAME:
* exclude has base (baselen long) implicitly
* in front of it.
*/
int baselen = x->baselen;
if (*exclude == '/')
exclude++;
if (pathlen < baselen ||
(baselen && pathname[baselen-1] != '/') ||
strncmp(pathname, x->base, baselen))
continue;
if (fnmatch(exclude, pathname+baselen,
FNM_PATHNAME) == 0)
return to_exclude;
}
}
}
return -1; /* undecided */
}
static int excluded(const char *pathname)
{
int pathlen = strlen(pathname);
int st;
for (st = EXC_CMDL; st <= EXC_FILE; st++) {
switch (excluded_1(pathname, pathlen, &exclude_list[st])) {
case 0:
return 0;
case 1:
return 1;
}
}
return 0;
}
struct nond_on_fs {
int len;
char name[FLEX_ARRAY]; /* more */
};
static struct nond_on_fs **dir;
static int nr_dir;
static int dir_alloc;
static void add_name(const char *pathname, int len)
{
struct nond_on_fs *ent;
if (cache_name_pos(pathname, len) >= 0)
return;
if (nr_dir == dir_alloc) {
dir_alloc = alloc_nr(dir_alloc);
dir = xrealloc(dir, dir_alloc*sizeof(ent));
}
ent = xmalloc(sizeof(*ent) + len + 1);
ent->len = len;
memcpy(ent->name, pathname, len);
ent->name[len] = 0;
dir[nr_dir++] = ent;
}
static int dir_exists(const char *dirname, int len)
{
int pos = cache_name_pos(dirname, len);
if (pos >= 0)
return 1;
pos = -pos-1;
if (pos >= active_nr) /* can't */
return 0;
return !strncmp(active_cache[pos]->name, dirname, len);
}
/*
* Read a directory tree. We currently ignore anything but
* directories, regular files and symlinks. That's because git
* doesn't handle them at all yet. Maybe that will change some
* day.
*
* Also, we ignore the name ".git" (even if it is not a directory).
* That likely will not change.
*/
static int read_directory(const char *path, const char *base, int baselen)
{
DIR *fdir = opendir(path);
int contents = 0;
if (fdir) {
int exclude_stk;
struct dirent *de;
char fullname[MAXPATHLEN + 1];
memcpy(fullname, base, baselen);
exclude_stk = push_exclude_per_directory(base, baselen);
while ((de = readdir(fdir)) != NULL) {
int len;
if ((de->d_name[0] == '.') &&
(de->d_name[1] == 0 ||
!strcmp(de->d_name + 1, ".") ||
!strcmp(de->d_name + 1, "git")))
continue;
len = strlen(de->d_name);
memcpy(fullname + baselen, de->d_name, len+1);
if (excluded(fullname) != show_ignored) {
if (!show_ignored || DTYPE(de) != DT_DIR) {
continue;
}
}
switch (DTYPE(de)) {
struct stat st;
int subdir, rewind_base;
default:
continue;
case DT_UNKNOWN:
if (lstat(fullname, &st))
continue;
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
break;
if (!S_ISDIR(st.st_mode))
continue;
/* fallthrough */
case DT_DIR:
memcpy(fullname + baselen + len, "/", 2);
len++;
rewind_base = nr_dir;
subdir = read_directory(fullname, fullname,
baselen + len);
if (show_other_directories &&
(subdir || !hide_empty_directories) &&
!dir_exists(fullname, baselen + len)) {
// Rewind the read subdirectory
while (nr_dir > rewind_base)
free(dir[--nr_dir]);
break;
}
contents += subdir;
continue;
case DT_REG:
case DT_LNK:
break;
}
add_name(fullname, baselen + len);
contents++;
}
closedir(fdir);
pop_exclude_per_directory(exclude_stk);
}
return contents;
}
static int cmp_name(const void *p1, const void *p2)
{
const struct nond_on_fs *e1 = *(const struct nond_on_fs **)p1;
const struct nond_on_fs *e2 = *(const struct nond_on_fs **)p2;
return cache_name_compare(e1->name, e1->len,
e2->name, e2->len);
}
/*
* Match a pathspec against a filename. The first "len" characters
@ -377,7 +72,7 @@ static int match(const char **spec, char *ps_matched,
return 0;
}
static void show_dir_entry(const char *tag, struct nond_on_fs *ent)
static void show_dir_entry(const char *tag, struct dir_entry *ent)
{
int len = prefix_len;
int offset = prefix_offset;
@ -393,14 +88,14 @@ static void show_dir_entry(const char *tag, struct nond_on_fs *ent)
putchar(line_terminator);
}
static void show_other_files(void)
static void show_other_files(struct dir_struct *dir)
{
int i;
for (i = 0; i < nr_dir; i++) {
for (i = 0; i < dir->nr; i++) {
/* We should not have a matching entry, but we
* may have an unmerged entry for this path.
*/
struct nond_on_fs *ent = dir[i];
struct dir_entry *ent = dir->entries[i];
int pos = cache_name_pos(ent->name, ent->len);
struct cache_entry *ce;
if (0 <= pos)
@ -416,11 +111,11 @@ static void show_other_files(void)
}
}
static void show_killed_files(void)
static void show_killed_files(struct dir_struct *dir)
{
int i;
for (i = 0; i < nr_dir; i++) {
struct nond_on_fs *ent = dir[i];
for (i = 0; i < dir->nr; i++) {
struct dir_entry *ent = dir->entries[i];
char *cp, *sp;
int pos, len, killed = 0;
@ -461,7 +156,7 @@ static void show_killed_files(void)
}
}
if (killed)
show_dir_entry(tag_killed, dir[i]);
show_dir_entry(tag_killed, dir->entries[i]);
}
}
@ -512,7 +207,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
}
}
static void show_files(void)
static void show_files(struct dir_struct *dir)
{
int i;
@ -521,39 +216,18 @@ static void show_files(void)
const char *path = ".", *base = "";
int baselen = prefix_len;
if (baselen) {
if (baselen)
path = base = prefix;
if (exclude_per_dir) {
char *p, *pp = xmalloc(baselen+1);
memcpy(pp, prefix, baselen+1);
p = pp;
while (1) {
char save = *p;
*p = 0;
push_exclude_per_directory(pp, p-pp);
*p++ = save;
if (!save)
break;
p = strchr(p, '/');
if (p)
p++;
else
p = pp + baselen;
}
free(pp);
}
}
read_directory(path, base, baselen);
qsort(dir, nr_dir, sizeof(struct nond_on_fs *), cmp_name);
read_directory(dir, path, base, baselen);
if (show_others)
show_other_files();
show_other_files(dir);
if (show_killed)
show_killed_files();
show_killed_files(dir);
}
if (show_cached | show_stage) {
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
if (excluded(ce->name) != show_ignored)
if (excluded(dir, ce->name) != dir->show_ignored)
continue;
if (show_unmerged && !ce_stage(ce))
continue;
@ -565,7 +239,7 @@ static void show_files(void)
struct cache_entry *ce = active_cache[i];
struct stat st;
int err;
if (excluded(ce->name) != show_ignored)
if (excluded(dir, ce->name) != dir->show_ignored)
continue;
err = lstat(ce->name, &st);
if (show_deleted && err)
@ -648,11 +322,13 @@ 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;
struct dir_struct dir;
memset(&dir, 0, sizeof(dir));
prefix = setup_git_directory();
if (prefix)
prefix_offset = strlen(prefix);
@ -697,7 +373,7 @@ int main(int argc, const char **argv)
continue;
}
if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) {
show_ignored = 1;
dir.show_ignored = 1;
continue;
}
if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) {
@ -709,11 +385,11 @@ int main(int argc, const char **argv)
continue;
}
if (!strcmp(arg, "--directory")) {
show_other_directories = 1;
dir.show_other_directories = 1;
continue;
}
if (!strcmp(arg, "--no-empty-directory")) {
hide_empty_directories = 1;
dir.hide_empty_directories = 1;
continue;
}
if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) {
@ -726,27 +402,27 @@ int main(int argc, const char **argv)
}
if (!strcmp(arg, "-x") && i+1 < argc) {
exc_given = 1;
add_exclude(argv[++i], "", 0, &exclude_list[EXC_CMDL]);
add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]);
continue;
}
if (!strncmp(arg, "--exclude=", 10)) {
exc_given = 1;
add_exclude(arg+10, "", 0, &exclude_list[EXC_CMDL]);
add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]);
continue;
}
if (!strcmp(arg, "-X") && i+1 < argc) {
exc_given = 1;
add_excludes_from_file(argv[++i]);
add_excludes_from_file(&dir, argv[++i]);
continue;
}
if (!strncmp(arg, "--exclude-from=", 15)) {
exc_given = 1;
add_excludes_from_file(arg+15);
add_excludes_from_file(&dir, arg+15);
continue;
}
if (!strncmp(arg, "--exclude-per-directory=", 24)) {
exc_given = 1;
exclude_per_dir = arg + 24;
dir.exclude_per_dir = arg + 24;
continue;
}
if (!strcmp(arg, "--full-name")) {
@ -788,7 +464,7 @@ int main(int argc, const char **argv)
ps_matched = xcalloc(1, num);
}
if (show_ignored && !exc_given) {
if (dir.show_ignored && !exc_given) {
fprintf(stderr, "%s: --ignored needs some exclude pattern\n",
argv[0]);
exit(1);
@ -802,7 +478,7 @@ int main(int argc, const char **argv)
read_cache();
if (prefix)
prune_cache();
show_files();
show_files(&dir);
if (ps_matched) {
/* We need to make sure all pathspec matched otherwise

Просмотреть файл

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

Просмотреть файл

@ -11,6 +11,7 @@
#include "tree.h"
#include <sys/time.h>
#include <signal.h>
#include "builtin.h"
static int reset = 0;
static int merge = 0;
@ -408,7 +409,7 @@ static void verify_uptodate(struct cache_entry *ce)
{
struct stat st;
if (index_only)
if (index_only || reset)
return;
if (!lstat(ce->name, &st)) {
@ -426,6 +427,21 @@ static void verify_uptodate(struct cache_entry *ce)
die("Entry '%s' not uptodate. Cannot merge.", ce->name);
}
/*
* We do not want to remove or overwrite a working tree file that
* is not tracked.
*/
static void verify_absent(const char *path, const char *action)
{
struct stat st;
if (index_only || reset || !update)
return;
if (!lstat(path, &st))
die("Untracked working tree file '%s' "
"would be %s by merge.", path, action);
}
static int merged_entry(struct cache_entry *merge, struct cache_entry *old)
{
merge->ce_flags |= htons(CE_UPDATE);
@ -443,6 +459,9 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old)
verify_uptodate(old);
}
}
else
verify_absent(merge->name, "overwritten");
merge->ce_flags &= ~htons(CE_STAGEMASK);
add_cache_entry(merge, ADD_CACHE_OK_TO_ADD);
return 1;
@ -452,6 +471,8 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old)
{
if (old)
verify_uptodate(old);
else
verify_absent(ce->name, "removed");
ce->ce_mode = 0;
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
return 1;
@ -487,6 +508,7 @@ static int threeway_merge(struct cache_entry **stages)
int count;
int head_match = 0;
int remote_match = 0;
const char *path = NULL;
int df_conflict_head = 0;
int df_conflict_remote = 0;
@ -498,8 +520,11 @@ static int threeway_merge(struct cache_entry **stages)
for (i = 1; i < head_idx; i++) {
if (!stages[i])
any_anc_missing = 1;
else
else {
if (!path)
path = stages[i]->name;
no_anc_exists = 0;
}
}
index = stages[0];
@ -515,8 +540,15 @@ static int threeway_merge(struct cache_entry **stages)
remote = NULL;
}
if (!path && index)
path = index->name;
if (!path && head)
path = head->name;
if (!path && remote)
path = remote->name;
/* First, if there's a #16 situation, note that to prevent #13
* and #14.
* and #14.
*/
if (!same(remote, head)) {
for (i = 1; i < head_idx; i++) {
@ -575,6 +607,8 @@ static int threeway_merge(struct cache_entry **stages)
(remote_deleted && head && head_match)) {
if (index)
return deleted_entry(index, index);
else if (path)
verify_absent(path, "removed");
return 0;
}
/*
@ -592,6 +626,8 @@ static int threeway_merge(struct cache_entry **stages)
if (index) {
verify_uptodate(index);
}
else if (path)
verify_absent(path, "overwritten");
nontrivial_merge = 1;
@ -689,7 +725,7 @@ static int oneway_merge(struct cache_entry **src)
merge_size);
if (!a)
return deleted_entry(old, NULL);
return deleted_entry(old, old);
if (old && same(old, a)) {
if (reset) {
struct stat st;
@ -699,7 +735,7 @@ static int oneway_merge(struct cache_entry **src)
}
return keep_entry(old);
}
return merged_entry(a, NULL);
return merged_entry(a, old);
}
static int read_cache_unmerged(void)
@ -728,7 +764,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];

Просмотреть файл

@ -7,6 +7,7 @@
#include "tree-walk.h"
#include "diff.h"
#include "revision.h"
#include "builtin.h"
/* bits #0-15 in revision.h */
@ -36,7 +37,7 @@ static const char rev_list_usage[] =
" --bisect"
;
struct rev_info revs;
static struct rev_info revs;
static int bisect_list = 0;
static int show_timestamp = 0;
@ -84,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);
@ -291,7 +292,7 @@ static void mark_edges_uninteresting(struct commit_list *list)
}
}
int main(int argc, const char **argv)
int cmd_rev_list(int argc, const char **argv, char **envp)
{
struct commit_list *list;
int i;

150
builtin-rm.c Normal file
Просмотреть файл

@ -0,0 +1,150 @@
/*
* "git rm" builtin command
*
* Copyright (C) Linus Torvalds 2006
*/
#include "cache.h"
#include "builtin.h"
#include "dir.h"
static const char builtin_rm_usage[] =
"git-rm [-n] [-v] [-f] <filepattern>...";
static struct {
int nr, alloc;
const char **name;
} list;
static void add_list(const char *name)
{
if (list.nr >= list.alloc) {
list.alloc = alloc_nr(list.alloc);
list.name = xrealloc(list.name, list.alloc * sizeof(const char *));
}
list.name[list.nr++] = name;
}
static int remove_file(const char *name)
{
int ret;
char *slash;
ret = unlink(name);
if (!ret && (slash = strrchr(name, '/'))) {
char *n = strdup(name);
do {
n[slash - name] = 0;
name = n;
} while (!rmdir(name) && (slash = strrchr(name, '/')));
}
return ret;
}
static struct cache_file cache_file;
int cmd_rm(int argc, const char **argv, char **envp)
{
int i, newfd;
int verbose = 0, show_only = 0, force = 0;
const char *prefix = setup_git_directory();
const char **pathspec;
char *seen;
git_config(git_default_config);
newfd = hold_index_file_for_update(&cache_file, get_index_file());
if (newfd < 0)
die("unable to create new index file");
if (read_cache() < 0)
die("index file corrupt");
for (i = 1 ; i < argc ; i++) {
const char *arg = argv[i];
if (*arg != '-')
break;
if (!strcmp(arg, "--")) {
i++;
break;
}
if (!strcmp(arg, "-n")) {
show_only = 1;
continue;
}
if (!strcmp(arg, "-v")) {
verbose = 1;
continue;
}
if (!strcmp(arg, "-f")) {
force = 1;
continue;
}
die(builtin_rm_usage);
}
pathspec = get_pathspec(prefix, argv + i);
seen = NULL;
if (pathspec) {
for (i = 0; pathspec[i] ; i++)
/* nothing */;
seen = xmalloc(i);
memset(seen, 0, i);
}
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
continue;
add_list(ce->name);
}
if (pathspec) {
const char *match;
for (i = 0; (match = pathspec[i]) != NULL ; i++) {
if (*match && !seen[i])
die("pathspec '%s' did not match any files", match);
}
}
/*
* First remove the names from the index: we won't commit
* the index unless all of them succeed
*/
for (i = 0; i < list.nr; i++) {
const char *path = list.name[i];
printf("rm '%s'\n", path);
if (remove_file_from_cache(path))
die("git rm: unable to remove %s", path);
}
/*
* Then, if we used "-f", remove the filenames from the
* workspace. If we fail to remove the first one, we
* abort the "git rm" (but once we've successfully removed
* any file at all, we'll go ahead and commit to it all:
* by then we've already committed ourself and can't fail
* in the middle)
*/
if (force) {
int removed = 0;
for (i = 0; i < list.nr; i++) {
const char *path = list.name[i];
if (!remove_file(path)) {
removed = 1;
continue;
}
if (!removed)
die("git rm: %s: %s", path, strerror(errno));
}
}
if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
commit_index_file(&cache_file))
die("Unable to write new index file");
}
return 0;
}

Просмотреть файл

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

Просмотреть файл

@ -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
Просмотреть файл

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

Просмотреть файл

@ -25,5 +25,23 @@ extern int cmd_count_objects(int argc, const char **argv, char **envp);
extern int cmd_push(int argc, const char **argv, char **envp);
extern int cmd_grep(int argc, const char **argv, char **envp);
extern int cmd_rm(int argc, const char **argv, char **envp);
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

Просмотреть файл

@ -142,6 +142,7 @@ extern void verify_non_filename(const char *prefix, const char *name);
/* Initialize and use the cache information */
extern int read_cache(void);
extern int write_cache(int newfd, struct cache_entry **cache, int entries);
extern int verify_path(const char *path);
extern int cache_name_pos(const char *name, int namelen);
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
@ -154,10 +155,17 @@ 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);
#define REFRESH_REALLY 0x0001 /* ignore_valid */
#define REFRESH_UNMERGED 0x0002 /* allow unmerged */
#define REFRESH_QUIET 0x0004 /* be quiet about it */
#define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */
extern int refresh_cache(unsigned int flags);
struct cache_file {
struct cache_file *next;
char lockfile[PATH_MAX];

Просмотреть файл

@ -1,17 +0,0 @@
/*
* GIT - The information manager from hell
*/
#include "cache.h"
#include "refs.h"
#include <stdio.h>
int main(int ac, char **av)
{
if (ac != 2)
usage("git-check-ref-format refname");
if (check_ref_format(av[1]))
exit(1);
return 0;
}

105
commit.c
Просмотреть файл

@ -422,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;
@ -440,12 +480,26 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
tz = strtol(date, NULL, 10);
if (fmt == CMIT_FMT_EMAIL) {
what = "From";
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);
}
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));
@ -498,20 +552,31 @@ 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 = 4;
int parents_shown = 0;
const char *msg = commit->buffer;
const char *subject = NULL;
int plain_non_ascii = 0;
if (fmt == CMIT_FMT_EMAIL)
subject = "Subject: [PATCH] ";
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;
int linelen = get_one_line(msg, len);
@ -587,13 +652,31 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
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;
}
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]))

Просмотреть файл

@ -51,7 +51,7 @@ enum cmit_fmt {
};
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
Просмотреть файл

@ -0,0 +1,200 @@
/*
* Copyright (c) 1996-1999 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#ifndef NS_INADDRSZ
#define NS_INADDRSZ 4
#endif
#ifndef NS_IN6ADDRSZ
#define NS_IN6ADDRSZ 16
#endif
#ifndef NS_INT16SZ
#define NS_INT16SZ 2
#endif
/*
* WARNING: Don't even consider trying to compile this on a system where
* sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
*/
/* const char *
* inet_ntop4(src, dst, size)
* format an IPv4 address
* return:
* `dst' (as a const)
* notes:
* (1) uses no statics
* (2) takes a u_char* not an in_addr as input
* author:
* Paul Vixie, 1996.
*/
static const char *
inet_ntop4(src, dst, size)
const u_char *src;
char *dst;
size_t size;
{
static const char fmt[] = "%u.%u.%u.%u";
char tmp[sizeof "255.255.255.255"];
int nprinted;
nprinted = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);
if (nprinted < 0)
return (NULL); /* we assume "errno" was set by "snprintf()" */
if ((size_t)nprinted > size) {
errno = ENOSPC;
return (NULL);
}
strcpy(dst, tmp);
return (dst);
}
#ifndef NO_IPV6
/* const char *
* inet_ntop6(src, dst, size)
* convert IPv6 binary address into presentation (printable) format
* author:
* Paul Vixie, 1996.
*/
static const char *
inet_ntop6(src, dst, size)
const u_char *src;
char *dst;
size_t size;
{
/*
* Note that int32_t and int16_t need only be "at least" large enough
* to contain a value of the specified size. On some systems, like
* Crays, there is no such thing as an integer variable with 16 bits.
* Keep this in mind if you think this function should have been coded
* to use pointer overlays. All the world's not a VAX.
*/
char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
struct { int base, len; } best, cur;
u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
int i;
/*
* Preprocess:
* Copy the input (bytewise) array into a wordwise array.
* Find the longest run of 0x00's in src[] for :: shorthanding.
*/
memset(words, '\0', sizeof words);
for (i = 0; i < NS_IN6ADDRSZ; i++)
words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
best.base = -1;
cur.base = -1;
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
if (words[i] == 0) {
if (cur.base == -1)
cur.base = i, cur.len = 1;
else
cur.len++;
} else {
if (cur.base != -1) {
if (best.base == -1 || cur.len > best.len)
best = cur;
cur.base = -1;
}
}
}
if (cur.base != -1) {
if (best.base == -1 || cur.len > best.len)
best = cur;
}
if (best.base != -1 && best.len < 2)
best.base = -1;
/*
* Format the result.
*/
tp = tmp;
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
/* Are we inside the best run of 0x00's? */
if (best.base != -1 && i >= best.base &&
i < (best.base + best.len)) {
if (i == best.base)
*tp++ = ':';
continue;
}
/* Are we following an initial run of 0x00s or any real hex? */
if (i != 0)
*tp++ = ':';
/* Is this address an encapsulated IPv4? */
if (i == 6 && best.base == 0 &&
(best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp)))
return (NULL);
tp += strlen(tp);
break;
}
tp += snprintf(tp, sizeof tmp - (tp - tmp), "%x", words[i]);
}
/* Was it a trailing run of 0x00's? */
if (best.base != -1 && (best.base + best.len) ==
(NS_IN6ADDRSZ / NS_INT16SZ))
*tp++ = ':';
*tp++ = '\0';
/*
* Check for overflow, copy, and we're done.
*/
if ((size_t)(tp - tmp) > size) {
errno = ENOSPC;
return (NULL);
}
strcpy(dst, tmp);
return (dst);
}
#endif
/* char *
* inet_ntop(af, src, dst, size)
* convert a network format address to presentation format.
* return:
* pointer to presentation format address (`dst'), or NULL (see errno).
* author:
* Paul Vixie, 1996.
*/
const char *
inet_ntop(af, src, dst, size)
int af;
const void *src;
char *dst;
size_t size;
{
switch (af) {
case AF_INET:
return (inet_ntop4(src, dst, size));
#ifndef NO_IPV6
case AF_INET6:
return (inet_ntop6(src, dst, size));
#endif
default:
errno = EAFNOSUPPORT;
return (NULL);
}
/* NOTREACHED */
}

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -0,0 +1,39 @@
PATH=$PWD/../:$PATH
if test -d ../../../t
then
cd ../../../t
else
echo "Must be run in contrib/git-svn/t" >&2
exit 1
fi
. ./test-lib.sh
GIT_DIR=$PWD/.git
GIT_SVN_DIR=$GIT_DIR/git-svn
SVN_TREE=$GIT_SVN_DIR/tree
svnadmin >/dev/null 2>&1
if test $? != 1
then
test_expect_success 'skipping contrib/git-svn test' :
test_done
exit
fi
svn >/dev/null 2>&1
if test $? != 1
then
test_expect_success 'skipping contrib/git-svn test' :
test_done
exit
fi
svnrepo=$PWD/svnrepo
set -e
svnadmin create $svnrepo
svnrepo="file://$svnrepo/test-git-svn"

Просмотреть файл

@ -3,48 +3,10 @@
# Copyright (c) 2006 Eric Wong
#
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 \

Просмотреть файл

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

35
contrib/remotes2config.sh Normal file
Просмотреть файл

@ -0,0 +1,35 @@
#!/bin/sh
# Use this tool to rewrite your .git/remotes/ files into the config.
. git-sh-setup
if [ -d "$GIT_DIR"/remotes ]; then
echo "Rewriting $GIT_DIR/remotes" >&2
error=0
# rewrite into config
{
cd "$GIT_DIR"/remotes
ls | while read f; do
name=$(echo -n "$f" | tr -c "A-Za-z0-9" ".")
sed -n \
-e "s/^URL: \(.*\)$/remote.$name.url \1 ./p" \
-e "s/^Pull: \(.*\)$/remote.$name.fetch \1 ^$ /p" \
-e "s/^Push: \(.*\)$/remote.$name.push \1 ^$ /p" \
< "$f"
done
echo done
} | while read key value regex; do
case $key in
done)
if [ $error = 0 ]; then
mv "$GIT_DIR"/remotes "$GIT_DIR"/remotes.old
fi ;;
*)
echo "git-repo-config $key "$value" $regex"
git-repo-config $key "$value" $regex || error=1 ;;
esac
done
fi

136
diff.c
Просмотреть файл

@ -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
Просмотреть файл

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

401
dir.c Normal file
Просмотреть файл

@ -0,0 +1,401 @@
/*
* This handles recursive filename detection with exclude
* files, index knowledge etc..
*
* Copyright (C) Linus Torvalds, 2005-2006
* Junio Hamano, 2005-2006
*/
#include <dirent.h>
#include <fnmatch.h>
#include "cache.h"
#include "dir.h"
int common_prefix(const char **pathspec)
{
const char *path, *slash, *next;
int prefix;
if (!pathspec)
return 0;
path = *pathspec;
slash = strrchr(path, '/');
if (!slash)
return 0;
prefix = slash - path + 1;
while ((next = *++pathspec) != NULL) {
int len = strlen(next);
if (len >= prefix && !memcmp(path, next, len))
continue;
for (;;) {
if (!len)
return 0;
if (next[--len] != '/')
continue;
if (memcmp(path, next, len+1))
continue;
prefix = len + 1;
break;
}
}
return prefix;
}
static int match_one(const char *match, const char *name, int namelen)
{
int matchlen;
/* If the match was just the prefix, we matched */
matchlen = strlen(match);
if (!matchlen)
return 1;
/*
* If we don't match the matchstring exactly,
* we need to match by fnmatch
*/
if (strncmp(match, name, matchlen))
return !fnmatch(match, name, 0);
/*
* If we did match the string exactly, we still
* need to make sure that it happened on a path
* component boundary (ie either the last character
* of the match was '/', or the next character of
* the name was '/' or the terminating NUL.
*/
return match[matchlen-1] == '/' ||
name[matchlen] == '/' ||
!name[matchlen];
}
int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen)
{
int retval;
const char *match;
name += prefix;
namelen -= prefix;
for (retval = 0; (match = *pathspec++) != NULL; seen++) {
if (retval & *seen)
continue;
match += prefix;
if (match_one(match, name, namelen)) {
retval = 1;
*seen = 1;
}
}
return retval;
}
void add_exclude(const char *string, const char *base,
int baselen, struct exclude_list *which)
{
struct exclude *x = xmalloc(sizeof (*x));
x->pattern = string;
x->base = base;
x->baselen = baselen;
if (which->nr == which->alloc) {
which->alloc = alloc_nr(which->alloc);
which->excludes = realloc(which->excludes,
which->alloc * sizeof(x));
}
which->excludes[which->nr++] = x;
}
static int add_excludes_from_file_1(const char *fname,
const char *base,
int baselen,
struct exclude_list *which)
{
int fd, i;
long size;
char *buf, *entry;
fd = open(fname, O_RDONLY);
if (fd < 0)
goto err;
size = lseek(fd, 0, SEEK_END);
if (size < 0)
goto err;
lseek(fd, 0, SEEK_SET);
if (size == 0) {
close(fd);
return 0;
}
buf = xmalloc(size+1);
if (read(fd, buf, size) != size)
goto err;
close(fd);
buf[size++] = '\n';
entry = buf;
for (i = 0; i < size; i++) {
if (buf[i] == '\n') {
if (entry != buf + i && entry[0] != '#') {
buf[i - (i && buf[i-1] == '\r')] = 0;
add_exclude(entry, base, baselen, which);
}
entry = buf + i + 1;
}
}
return 0;
err:
if (0 <= fd)
close(fd);
return -1;
}
void add_excludes_from_file(struct dir_struct *dir, const char *fname)
{
if (add_excludes_from_file_1(fname, "", 0,
&dir->exclude_list[EXC_FILE]) < 0)
die("cannot use %s as an exclude file", fname);
}
static int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen)
{
char exclude_file[PATH_MAX];
struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
int current_nr = el->nr;
if (dir->exclude_per_dir) {
memcpy(exclude_file, base, baselen);
strcpy(exclude_file + baselen, dir->exclude_per_dir);
add_excludes_from_file_1(exclude_file, base, baselen, el);
}
return current_nr;
}
static void pop_exclude_per_directory(struct dir_struct *dir, int stk)
{
struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
while (stk < el->nr)
free(el->excludes[--el->nr]);
}
/* Scan the list and let the last match determines the fate.
* Return 1 for exclude, 0 for include and -1 for undecided.
*/
static int excluded_1(const char *pathname,
int pathlen,
struct exclude_list *el)
{
int i;
if (el->nr) {
for (i = el->nr - 1; 0 <= i; i--) {
struct exclude *x = el->excludes[i];
const char *exclude = x->pattern;
int to_exclude = 1;
if (*exclude == '!') {
to_exclude = 0;
exclude++;
}
if (!strchr(exclude, '/')) {
/* match basename */
const char *basename = strrchr(pathname, '/');
basename = (basename) ? basename+1 : pathname;
if (fnmatch(exclude, basename, 0) == 0)
return to_exclude;
}
else {
/* match with FNM_PATHNAME:
* exclude has base (baselen long) implicitly
* in front of it.
*/
int baselen = x->baselen;
if (*exclude == '/')
exclude++;
if (pathlen < baselen ||
(baselen && pathname[baselen-1] != '/') ||
strncmp(pathname, x->base, baselen))
continue;
if (fnmatch(exclude, pathname+baselen,
FNM_PATHNAME) == 0)
return to_exclude;
}
}
}
return -1; /* undecided */
}
int excluded(struct dir_struct *dir, const char *pathname)
{
int pathlen = strlen(pathname);
int st;
for (st = EXC_CMDL; st <= EXC_FILE; st++) {
switch (excluded_1(pathname, pathlen, &dir->exclude_list[st])) {
case 0:
return 0;
case 1:
return 1;
}
}
return 0;
}
static void add_name(struct dir_struct *dir, const char *pathname, int len)
{
struct dir_entry *ent;
if (cache_name_pos(pathname, len) >= 0)
return;
if (dir->nr == dir->alloc) {
int alloc = alloc_nr(dir->alloc);
dir->alloc = alloc;
dir->entries = xrealloc(dir->entries, alloc*sizeof(ent));
}
ent = xmalloc(sizeof(*ent) + len + 1);
ent->len = len;
memcpy(ent->name, pathname, len);
ent->name[len] = 0;
dir->entries[dir->nr++] = ent;
}
static int dir_exists(const char *dirname, int len)
{
int pos = cache_name_pos(dirname, len);
if (pos >= 0)
return 1;
pos = -pos-1;
if (pos >= active_nr) /* can't */
return 0;
return !strncmp(active_cache[pos]->name, dirname, len);
}
/*
* Read a directory tree. We currently ignore anything but
* directories, regular files and symlinks. That's because git
* doesn't handle them at all yet. Maybe that will change some
* day.
*
* Also, we ignore the name ".git" (even if it is not a directory).
* That likely will not change.
*/
static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen)
{
DIR *fdir = opendir(path);
int contents = 0;
if (fdir) {
int exclude_stk;
struct dirent *de;
char fullname[MAXPATHLEN + 1];
memcpy(fullname, base, baselen);
exclude_stk = push_exclude_per_directory(dir, base, baselen);
while ((de = readdir(fdir)) != NULL) {
int len;
if ((de->d_name[0] == '.') &&
(de->d_name[1] == 0 ||
!strcmp(de->d_name + 1, ".") ||
!strcmp(de->d_name + 1, "git")))
continue;
len = strlen(de->d_name);
memcpy(fullname + baselen, de->d_name, len+1);
if (excluded(dir, fullname) != dir->show_ignored) {
if (!dir->show_ignored || DTYPE(de) != DT_DIR) {
continue;
}
}
switch (DTYPE(de)) {
struct stat st;
int subdir, rewind_base;
default:
continue;
case DT_UNKNOWN:
if (lstat(fullname, &st))
continue;
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
break;
if (!S_ISDIR(st.st_mode))
continue;
/* fallthrough */
case DT_DIR:
memcpy(fullname + baselen + len, "/", 2);
len++;
rewind_base = dir->nr;
subdir = read_directory_recursive(dir, fullname, fullname,
baselen + len);
if (dir->show_other_directories &&
(subdir || !dir->hide_empty_directories) &&
!dir_exists(fullname, baselen + len)) {
// Rewind the read subdirectory
while (dir->nr > rewind_base)
free(dir->entries[--dir->nr]);
break;
}
contents += subdir;
continue;
case DT_REG:
case DT_LNK:
break;
}
add_name(dir, fullname, baselen + len);
contents++;
}
closedir(fdir);
pop_exclude_per_directory(dir, exclude_stk);
}
return contents;
}
static int cmp_name(const void *p1, const void *p2)
{
const struct dir_entry *e1 = *(const struct dir_entry **)p1;
const struct dir_entry *e2 = *(const struct dir_entry **)p2;
return cache_name_compare(e1->name, e1->len,
e2->name, e2->len);
}
int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen)
{
/*
* Make sure to do the per-directory exclude for all the
* directories leading up to our base.
*/
if (baselen) {
if (dir->exclude_per_dir) {
char *p, *pp = xmalloc(baselen+1);
memcpy(pp, base, baselen+1);
p = pp;
while (1) {
char save = *p;
*p = 0;
push_exclude_per_directory(dir, pp, p-pp);
*p++ = save;
if (!save)
break;
p = strchr(p, '/');
if (p)
p++;
else
p = pp + baselen;
}
free(pp);
}
}
read_directory_recursive(dir, path, base, baselen);
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
return dir->nr;
}

51
dir.h Normal file
Просмотреть файл

@ -0,0 +1,51 @@
#ifndef DIR_H
#define DIR_H
/*
* We maintain three exclude pattern lists:
* EXC_CMDL lists patterns explicitly given on the command line.
* EXC_DIRS lists patterns obtained from per-directory ignore files.
* EXC_FILE lists patterns from fallback ignore files.
*/
#define EXC_CMDL 0
#define EXC_DIRS 1
#define EXC_FILE 2
struct dir_entry {
int len;
char name[FLEX_ARRAY]; /* more */
};
struct exclude_list {
int nr;
int alloc;
struct exclude {
const char *pattern;
const char *base;
int baselen;
} **excludes;
};
struct dir_struct {
int nr, alloc;
unsigned int show_ignored:1,
show_other_directories:1,
hide_empty_directories:1;
struct dir_entry **entries;
/* Exclude info */
const char *exclude_per_dir;
struct exclude_list exclude_list[3];
};
extern int common_prefix(const char **pathspec);
extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen);
extern int excluded(struct dir_struct *, const char *);
extern void add_excludes_from_file(struct dir_struct *, const char *fname);
extern void add_exclude(const char *string, const char *base,
int baselen, struct exclude_list *which);
#endif

Просмотреть файл

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

Просмотреть файл

@ -10,8 +10,6 @@
const char *write_ref = NULL;
const char *write_ref_log_details = NULL;
const unsigned char *current_ref = NULL;
int get_tree = 0;
int get_history = 0;
int get_all = 0;
@ -213,16 +211,15 @@ int pull(char *target)
save_commit_buffer = 0;
track_object_refs = 0;
if (write_ref) {
lock = lock_ref_sha1(write_ref, current_ref, 1);
lock = lock_ref_sha1(write_ref, NULL, 0);
if (!lock) {
error("Can't lock ref %s", write_ref);
return -1;
}
}
if (!get_recover) {
if (!get_recover)
for_each_ref(mark_complete);
}
if (interpret_target(target, sha1)) {
error("Could not interpret %s as something to pull", target);

Просмотреть файл

@ -28,9 +28,6 @@ extern const char *write_ref;
/* If set additional text will appear in the ref log. */
extern const char *write_ref_log_details;
/* 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;

Просмотреть файл

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

Просмотреть файл

@ -1,56 +0,0 @@
#!/bin/sh
USAGE='[-n] [-v] <file>...'
SUBDIRECTORY_OK='Yes'
. git-sh-setup
show_only=
verbose=
while : ; do
case "$1" in
-n)
show_only=true
;;
-v)
verbose=--verbose
;;
--)
shift
break
;;
-*)
usage
;;
*)
break
;;
esac
shift
done
# Check misspelled pathspec
case "$#" in
0) ;;
*)
git-ls-files --error-unmatch --others --cached -- "$@" >/dev/null || {
echo >&2 "Maybe you misspelled it?"
exit 1
}
;;
esac
if test -f "$GIT_DIR/info/exclude"
then
git-ls-files -z \
--exclude-from="$GIT_DIR/info/exclude" \
--others --exclude-per-directory=.gitignore -- "$@"
else
git-ls-files -z \
--others --exclude-per-directory=.gitignore -- "$@"
fi |
case "$show_only" in
true)
xargs -0 echo ;;
*)
git-update-index --add $verbose -z --stdin ;;
esac

Просмотреть файл

@ -59,46 +59,12 @@ fall_back_3way () {
GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
git-write-tree >"$dotest/patch-merge-base+" &&
# index has the base tree now.
(
cd "$dotest/patch-merge-tmp-dir" &&
GIT_INDEX_FILE="../patch-merge-tmp-index" \
GIT_OBJECT_DIRECTORY="$O_OBJECT" \
git-apply $binary --index <../patch
)
GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
git-apply $binary --cached <"$dotest/patch"
then
echo Using index info to reconstruct a base tree...
mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
else
# Otherwise, try nearby trees that can be used to apply the
# patch.
(
N=10
# Hoping the patch is against our recent commits...
git-rev-list --max-count=$N HEAD
# or hoping the patch is against known tags...
git-ls-remote --tags .
) |
while read base junk
do
# See if we have it as a tree...
git-cat-file tree "$base" >/dev/null 2>&1 || continue
rm -fr "$dotest"/patch-merge-* &&
mkdir "$dotest/patch-merge-tmp-dir" || break
(
cd "$dotest/patch-merge-tmp-dir" &&
GIT_INDEX_FILE=../patch-merge-tmp-index &&
GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
git-read-tree "$base" &&
git-apply $binary --index &&
mv ../patch-merge-tmp-index ../patch-merge-index &&
echo "$base" >../patch-merge-base
) <"$dotest/patch" 2>/dev/null && break
done
fi
test -f "$dotest/patch-merge-index" &&

Просмотреть файл

@ -199,7 +199,7 @@ 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" ;;
@ -407,5 +407,5 @@ Pull: refs/heads/$head_points_at:$origin_track" &&
fi
rm -f "$GIT_DIR/CLONE_HEAD" "$GIT_DIR/REMOTE_HEAD"
trap - exit
trap - 0

Просмотреть файл

@ -3,7 +3,7 @@
# Copyright (c) 2005 Linus Torvalds
# Copyright (c) 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

Просмотреть файл

@ -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 :errno_h);
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") {
@ -350,7 +335,7 @@ sub _line {
return $res;
} elsif($line =~ s/^E //) {
# print STDERR "S: $line\n";
} elsif($line =~ /^Remove-entry /i) {
} elsif($line =~ /^(Remove-entry|Removed) /i) {
$line = $self->readline(); # filename
$line = $self->readline(); # OK
chomp $line;
@ -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);

Просмотреть файл

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

Просмотреть файл

@ -10,7 +10,10 @@ get_data_source () {
# Not so fast. This could be the partial URL shorthand...
token=$(expr "z$1" : 'z\([^/]*\)/')
remainder=$(expr "z$1" : 'z[^/]*/\(.*\)')
if test -f "$GIT_DIR/branches/$token"
if test "$(git-repo-config --get "remote.$token.url")"
then
echo config-partial
elif test -f "$GIT_DIR/branches/$token"
then
echo branches-partial
else
@ -18,7 +21,10 @@ get_data_source () {
fi
;;
*)
if test -f "$GIT_DIR/remotes/$1"
if test "$(git-repo-config --get "remote.$1.url")"
then
echo config
elif test -f "$GIT_DIR/remotes/$1"
then
echo remotes
elif test -f "$GIT_DIR/branches/$1"
@ -35,6 +41,15 @@ get_remote_url () {
case "$data_source" in
'')
echo "$1" ;;
config-partial)
token=$(expr "z$1" : 'z\([^/]*\)/')
remainder=$(expr "z$1" : 'z[^/]*/\(.*\)')
url=$(git-repo-config --get "remote.$token.url")
echo "$url/$remainder"
;;
config)
git-repo-config --get "remote.$1.url"
;;
remotes)
sed -ne '/^URL: */{
s///p
@ -56,8 +71,10 @@ get_remote_url () {
get_remote_default_refs_for_push () {
data_source=$(get_data_source "$1")
case "$data_source" in
'' | branches | branches-partial)
'' | config-partial | branches | branches-partial)
;; # no default push mapping, just send matching refs.
config)
git-repo-config --get-all "remote.$1.push" ;;
remotes)
sed -ne '/^Push: */{
s///p
@ -111,8 +128,11 @@ canon_refs_list_for_fetch () {
get_remote_default_refs_for_fetch () {
data_source=$(get_data_source "$1")
case "$data_source" in
'' | branches-partial)
'' | config-partial | branches-partial)
echo "HEAD:" ;;
config)
canon_refs_list_for_fetch \
$(git-repo-config --get-all "remote.$1.fetch") ;;
branches)
remote_branch=$(sed -ne '/#/s/.*#//p' "$GIT_DIR/branches/$1")
case "$remote_branch" in '') remote_branch=master ;; esac

118
git-quiltimport.sh Executable file
Просмотреть файл

@ -0,0 +1,118 @@
#!/bin/sh
USAGE='--dry-run --author <author> --patches </path/to/quilt/patch/directory>'
SUBDIRECTORY_ON=Yes
. git-sh-setup
dry_run=""
quilt_author=""
while case "$#" in 0) break;; esac
do
case "$1" in
--au=*|--aut=*|--auth=*|--autho=*|--author=*)
quilt_author=$(expr "$1" : '-[^=]*\(.*\)')
shift
;;
--au|--aut|--auth|--autho|--author)
case "$#" in 1) usage ;; esac
shift
quilt_author="$1"
shift
;;
--dry-run)
shift
dry_run=1
;;
--pa=*|--pat=*|--patc=*|--patch=*|--patche=*|--patches=*)
QUILT_PATCHES=$(expr "$1" : '-[^=]*\(.*\)')
shift
;;
--pa|--pat|--patc|--patch|--patche|--patches)
case "$#" in 1) usage ;; esac
shift
QUILT_PATCHES="$1"
shift
;;
*)
break
;;
esac
done
# Quilt Author
if [ -n "$quilt_author" ] ; then
quilt_author_name=$(expr "z$quilt_author" : 'z\(.*[^ ]\) *<.*') &&
quilt_author_email=$(expr "z$quilt_author" : '.*<\([^>]*\)') &&
test '' != "$quilt_author_name" &&
test '' != "$quilt_author_email" ||
die "malformatted --author parameter"
fi
# Quilt patch directory
: ${QUILT_PATCHES:=patches}
if ! [ -d "$QUILT_PATCHES" ] ; then
echo "The \"$QUILT_PATCHES\" directory does not exist."
exit 1
fi
# Temporay directories
tmp_dir=.dotest
tmp_msg="$tmp_dir/msg"
tmp_patch="$tmp_dir/patch"
tmp_info="$tmp_dir/info"
# Find the intial commit
commit=$(git-rev-parse HEAD)
mkdir $tmp_dir || exit 2
for patch_name in $(cat "$QUILT_PATCHES/series" | grep -v '^#'); do
echo $patch_name
(cat $QUILT_PATCHES/$patch_name | git-mailinfo "$tmp_msg" "$tmp_patch" > "$tmp_info") || exit 3
# Parse the author information
export GIT_AUTHOR_NAME=$(sed -ne 's/Author: //p' "$tmp_info")
export GIT_AUTHOR_EMAIL=$(sed -ne 's/Email: //p' "$tmp_info")
while test -z "$GIT_AUTHOR_EMAIL" && test -z "$GIT_AUTHOR_NAME" ; do
if [ -n "$quilt_author" ] ; then
GIT_AUTHOR_NAME="$quilt_author_name";
GIT_AUTHOR_EMAIL="$quilt_author_email";
elif [ -n "$dry_run" ]; then
echo "No author found in $patch_name" >&2;
GIT_AUTHOR_NAME="dry-run-not-found";
GIT_AUTHOR_EMAIL="dry-run-not-found";
else
echo "No author found in $patch_name" >&2;
echo "---"
cat $tmp_msg
echo -n "Author: ";
read patch_author
echo "$patch_author"
patch_author_name=$(expr "z$patch_author" : 'z\(.*[^ ]\) *<.*') &&
patch_author_email=$(expr "z$patch_author" : '.*<\([^>]*\)') &&
test '' != "$patch_author_name" &&
test '' != "$patch_author_email" &&
GIT_AUTHOR_NAME="$patch_author_name" &&
GIT_AUTHOR_EMAIL="$patch_author_email"
fi
done
export GIT_AUTHOR_DATE=$(sed -ne 's/Date: //p' "$tmp_info")
export SUBJECT=$(sed -ne 's/Subject: //p' "$tmp_info")
if [ -z "$SUBJECT" ] ; then
SUBJECT=$(echo $patch_name | sed -e 's/.patch$//')
fi
if [ -z "$dry_run" ] ; then
git-apply --index -C1 "$tmp_patch" &&
tree=$(git-write-tree) &&
commit=$((echo "$SUBJECT"; echo; cat "$tmp_msg") | git-commit-tree $tree -p $commit) &&
git-update-ref HEAD $commit || exit 4
fi
done
rm -rf $tmp_dir || exit 5

Просмотреть файл

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

Просмотреть файл

@ -1,70 +0,0 @@
#!/bin/sh
USAGE='[-f] [-n] [-v] [--] <file>...'
SUBDIRECTORY_OK='Yes'
. git-sh-setup
remove_files=
show_only=
verbose=
while : ; do
case "$1" in
-f)
remove_files=true
;;
-n)
show_only=true
;;
-v)
verbose=--verbose
;;
--)
shift; break
;;
-*)
usage
;;
*)
break
;;
esac
shift
done
# This is typo-proofing. If some paths match and some do not, we want
# to do nothing.
case "$#" in
0) ;;
*)
git-ls-files --error-unmatch -- "$@" >/dev/null || {
echo >&2 "Maybe you misspelled it?"
exit 1
}
;;
esac
if test -f "$GIT_DIR/info/exclude"
then
git-ls-files -z \
--exclude-from="$GIT_DIR/info/exclude" \
--exclude-per-directory=.gitignore -- "$@"
else
git-ls-files -z \
--exclude-per-directory=.gitignore -- "$@"
fi |
case "$show_only,$remove_files" in
true,*)
xargs -0 echo
;;
*,true)
xargs -0 sh -c "
while [ \$# -gt 0 ]; do
file=\$1; shift
rm -- \"\$file\" && git-update-index --remove $verbose \"\$file\"
done
" inline
;;
*)
git-update-index --force-remove $verbose -z --stdin
;;
esac

21
git.c
Просмотреть файл

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

Просмотреть файл

@ -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) {
@ -50,10 +51,50 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
* Print header line of header..
*/
if (opt->commit_format == CMIT_FMT_EMAIL)
printf("From %s Thu Apr 7 15:13:13 2005\n",
sha1_to_hex(commit->object.sha1));
else {
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));
@ -69,7 +110,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
/*
* 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);
}

45
mktag.c
Просмотреть файл

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

Просмотреть файл

@ -331,6 +331,70 @@ int ce_path_match(const struct cache_entry *ce, const char **pathspec)
return 0;
}
/*
* We fundamentally don't like some paths: we don't want
* dot or dot-dot anywhere, and for obvious reasons don't
* want to recurse into ".git" either.
*
* Also, we don't want double slashes or slashes at the
* end that can make pathnames ambiguous.
*/
static int verify_dotfile(const char *rest)
{
/*
* The first character was '.', but that
* has already been discarded, we now test
* the rest.
*/
switch (*rest) {
/* "." is not allowed */
case '\0': case '/':
return 0;
/*
* ".git" followed by NUL or slash is bad. This
* shares the path end test with the ".." case.
*/
case 'g':
if (rest[1] != 'i')
break;
if (rest[2] != 't')
break;
rest += 2;
/* fallthrough */
case '.':
if (rest[1] == '\0' || rest[1] == '/')
return 0;
}
return 1;
}
int verify_path(const char *path)
{
char c;
goto inside;
for (;;) {
if (!c)
return 1;
if (c == '/') {
inside:
c = *path++;
switch (c) {
default:
continue;
case '/': case '\0':
break;
case '.':
if (verify_dotfile(path))
continue;
}
return 0;
}
c = *path++;
}
}
/*
* Do we have another file that has the beginning components being a
* proper superset of the name we're trying to add?
@ -472,6 +536,8 @@ int add_cache_entry(struct cache_entry *ce, int option)
if (!ok_to_add)
return -1;
if (!verify_path(ce->name))
return -1;
if (!skip_df_check &&
check_file_directory_conflict(ce, pos, ok_to_replace)) {
@ -496,6 +562,123 @@ int add_cache_entry(struct cache_entry *ce, int option)
return 0;
}
/* Three functions to allow overloaded pointer return; see linux/err.h */
static inline void *ERR_PTR(long error)
{
return (void *) error;
}
static inline long PTR_ERR(const void *ptr)
{
return (long) ptr;
}
static inline long IS_ERR(const void *ptr)
{
return (unsigned long)ptr > (unsigned long)-1000L;
}
/*
* "refresh" does not calculate a new sha1 file or bring the
* cache up-to-date for mode/content changes. But what it
* _does_ do is to "re-match" the stat information of a file
* with the cache, so that you can refresh the cache for a
* file that hasn't been changed but where the stat entry is
* out of date.
*
* For example, you'd want to do this after doing a "git-read-tree",
* to link up the stat cache details with the proper files.
*/
static struct cache_entry *refresh_entry(struct cache_entry *ce, int really)
{
struct stat st;
struct cache_entry *updated;
int changed, size;
if (lstat(ce->name, &st) < 0)
return ERR_PTR(-errno);
changed = ce_match_stat(ce, &st, really);
if (!changed) {
if (really && assume_unchanged &&
!(ce->ce_flags & htons(CE_VALID)))
; /* mark this one VALID again */
else
return NULL;
}
if (ce_modified(ce, &st, really))
return ERR_PTR(-EINVAL);
size = ce_size(ce);
updated = xmalloc(size);
memcpy(updated, ce, size);
fill_stat_cache_info(updated, &st);
/* In this case, if really is not set, we should leave
* CE_VALID bit alone. Otherwise, paths marked with
* --no-assume-unchanged (i.e. things to be edited) will
* reacquire CE_VALID bit automatically, which is not
* really what we want.
*/
if (!really && assume_unchanged && !(ce->ce_flags & htons(CE_VALID)))
updated->ce_flags &= ~htons(CE_VALID);
return updated;
}
int refresh_cache(unsigned int flags)
{
int i;
int has_errors = 0;
int really = (flags & REFRESH_REALLY) != 0;
int allow_unmerged = (flags & REFRESH_UNMERGED) != 0;
int quiet = (flags & REFRESH_QUIET) != 0;
int not_new = (flags & REFRESH_IGNORE_MISSING) != 0;
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce, *new;
ce = active_cache[i];
if (ce_stage(ce)) {
while ((i < active_nr) &&
! strcmp(active_cache[i]->name, ce->name))
i++;
i--;
if (allow_unmerged)
continue;
printf("%s: needs merge\n", ce->name);
has_errors = 1;
continue;
}
new = refresh_entry(ce, really);
if (!new)
continue;
if (IS_ERR(new)) {
if (not_new && PTR_ERR(new) == -ENOENT)
continue;
if (really && PTR_ERR(new) == -EINVAL) {
/* If we are doing --really-refresh that
* means the index is not valid anymore.
*/
ce->ce_flags &= ~htons(CE_VALID);
active_cache_changed = 1;
}
if (quiet)
continue;
printf("%s: needs update\n", ce->name);
has_errors = 1;
continue;
}
active_cache_changed = 1;
/* You can NOT just free active_cache[i] here, since it
* might not be necessarily malloc()ed but can also come
* from mmap(). */
active_cache[i] = new;
}
return has_errors;
}
static int verify_hdr(struct cache_header *hdr, unsigned long size)
{
SHA_CTX c;

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -39,7 +39,6 @@ test_expect_success \
echo nitfol >nitfol &&
echo bozbar >bozbar &&
echo rezrov >rezrov &&
echo yomin >yomin &&
git-update-index --add nitfol bozbar rezrov &&
treeH=`git-write-tree` &&
echo treeH $treeH &&
@ -56,7 +55,8 @@ test_expect_success \
test_expect_success \
'1, 2, 3 - no carry forward' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >1-3.out &&
cmp M.out 1-3.out &&
@ -66,11 +66,12 @@ test_expect_success \
check_cache_at frotz clean &&
check_cache_at nitfol clean'
echo '+100644 X 0 yomin' >expected
test_expect_success \
'4 - carry forward local addition.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo "+100644 X 0 yomin" >expected &&
echo yomin >yomin &&
git-update-index --add yomin &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >4.out || return 1
@ -85,7 +86,9 @@ test_expect_success \
test_expect_success \
'5 - carry forward local addition.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
git-read-tree -m -u $treeH &&
echo yomin >yomin &&
git-update-index --add yomin &&
echo yomin yomin >yomin &&
@ -103,7 +106,9 @@ test_expect_success \
test_expect_success \
'6 - local addition already has the same.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo frotz >frotz &&
git-update-index --add frotz &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >6.out &&
@ -117,7 +122,8 @@ test_expect_success \
test_expect_success \
'7 - local addition already has the same.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo frotz >frotz &&
git-update-index --add frotz &&
echo frotz frotz >frotz &&
@ -134,14 +140,16 @@ test_expect_success \
test_expect_success \
'8 - conflicting addition.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo frotz frotz >frotz &&
git-update-index --add frotz &&
if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
test_expect_success \
'9 - conflicting addition.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo frotz frotz >frotz &&
git-update-index --add frotz &&
echo frotz >frotz &&
@ -149,7 +157,8 @@ test_expect_success \
test_expect_success \
'10 - path removed.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo rezrov >rezrov &&
git-update-index --add rezrov &&
git-read-tree -m -u $treeH $treeM &&
@ -160,7 +169,8 @@ test_expect_success \
test_expect_success \
'11 - dirty path removed.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo rezrov >rezrov &&
git-update-index --add rezrov &&
echo rezrov rezrov >rezrov &&
@ -168,14 +178,16 @@ test_expect_success \
test_expect_success \
'12 - unmatching local changes being removed.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo rezrov rezrov >rezrov &&
git-update-index --add rezrov &&
if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
test_expect_success \
'13 - unmatching local changes being removed.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo rezrov rezrov >rezrov &&
git-update-index --add rezrov &&
echo rezrov >rezrov &&
@ -188,7 +200,8 @@ EOF
test_expect_success \
'14 - unchanged in two heads.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo nitfol nitfol >nitfol &&
git-update-index --add nitfol &&
git-read-tree -m -u $treeH $treeM &&
@ -207,7 +220,8 @@ test_expect_success \
test_expect_success \
'15 - unchanged in two heads.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo nitfol nitfol >nitfol &&
git-update-index --add nitfol &&
echo nitfol nitfol nitfol >nitfol &&
@ -227,14 +241,16 @@ test_expect_success \
test_expect_success \
'16 - conflicting local change.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo bozbar bozbar >bozbar &&
git-update-index --add bozbar &&
if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
test_expect_success \
'17 - conflicting local change.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo bozbar bozbar >bozbar &&
git-update-index --add bozbar &&
echo bozbar bozbar bozbar >bozbar &&
@ -242,7 +258,8 @@ test_expect_success \
test_expect_success \
'18 - local change already having a good result.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo gnusto >bozbar &&
git-update-index --add bozbar &&
git-read-tree -m -u $treeH $treeM &&
@ -254,7 +271,8 @@ test_expect_success \
test_expect_success \
'19 - local change already having a good result, further modified.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo gnusto >bozbar &&
git-update-index --add bozbar &&
echo gnusto gnusto >bozbar &&
@ -273,7 +291,8 @@ test_expect_success \
test_expect_success \
'20 - no local change, use new tree.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo bozbar >bozbar &&
git-update-index --add bozbar &&
git-read-tree -m -u $treeH $treeM &&
@ -285,7 +304,8 @@ test_expect_success \
test_expect_success \
'21 - no local change, dirty cache.' \
'rm -f .git/index &&
'rm -f .git/index nitfol bozbar rezrov frotz &&
git-read-tree --reset -u $treeH &&
echo bozbar >bozbar &&
git-update-index --add bozbar &&
echo gnusto gnusto >bozbar &&
@ -294,7 +314,7 @@ test_expect_success \
# Also make sure we did not break DF vs DF/DF case.
test_expect_success \
'DF vs DF/DF case setup.' \
'rm -f .git/index &&
'rm -f .git/index
echo DF >DF &&
git-update-index --add DF &&
treeDF=`git-write-tree` &&

Просмотреть файл

@ -30,6 +30,7 @@ test_expect_success \
git-commit -m "Add C." &&
git-checkout -f master &&
rm -f B C &&
echo Third >> A &&
git-update-index A &&

Просмотреть файл

@ -191,7 +191,7 @@ test_expect_success \
'rm -fr Z [A-Z][A-Z] &&
git-read-tree $tree_A &&
git-checkout-index -f -a &&
git-read-tree -m $tree_O || return 1
git-read-tree --reset $tree_O || return 1
git-update-index --refresh >/dev/null ;# this can exit non-zero
git-diff-files >.test-a &&
cmp_diff_files_output .test-a .test-recursive-OA'
@ -201,7 +201,7 @@ test_expect_success \
'rm -fr Z [A-Z][A-Z] &&
git-read-tree $tree_B &&
git-checkout-index -f -a &&
git-read-tree -m $tree_O || return 1
git-read-tree --reset $tree_O || return 1
git-update-index --refresh >/dev/null ;# this can exit non-zero
git-diff-files >.test-a &&
cmp_diff_files_output .test-a .test-recursive-OB'
@ -211,7 +211,7 @@ test_expect_success \
'rm -fr Z [A-Z][A-Z] &&
git-read-tree $tree_B &&
git-checkout-index -f -a &&
git-read-tree -m $tree_A || return 1
git-read-tree --reset $tree_A || return 1
git-update-index --refresh >/dev/null ;# this can exit non-zero
git-diff-files >.test-a &&
cmp_diff_files_output .test-a .test-recursive-AB'

Просмотреть файл

@ -111,6 +111,7 @@ test_expect_success 'pull renaming branch into unrenaming one' \
test_expect_success 'pull renaming branch into another renaming one' \
'
rm -f B
git reset --hard
git checkout red
git pull . white && {

Просмотреть файл

@ -18,9 +18,6 @@
static int allow_add;
static int allow_remove;
static int allow_replace;
static int allow_unmerged; /* --refresh needing merge is not error */
static int not_new; /* --refresh not having working tree files is not error */
static int quiet; /* --refresh needing update is not error */
static int info_only;
static int force_remove;
static int verbose;
@ -28,23 +25,6 @@ static int mark_valid_only = 0;
#define MARK_VALID 1
#define UNMARK_VALID 2
/* Three functions to allow overloaded pointer return; see linux/err.h */
static inline void *ERR_PTR(long error)
{
return (void *) error;
}
static inline long PTR_ERR(const void *ptr)
{
return (long) ptr;
}
static inline long IS_ERR(const void *ptr)
{
return (unsigned long)ptr > (unsigned long)-1000L;
}
static void report(const char *fmt, ...)
{
va_list vp;
@ -140,167 +120,6 @@ static int add_file_to_cache(const char *path)
return 0;
}
/*
* "refresh" does not calculate a new sha1 file or bring the
* cache up-to-date for mode/content changes. But what it
* _does_ do is to "re-match" the stat information of a file
* with the cache, so that you can refresh the cache for a
* file that hasn't been changed but where the stat entry is
* out of date.
*
* For example, you'd want to do this after doing a "git-read-tree",
* to link up the stat cache details with the proper files.
*/
static struct cache_entry *refresh_entry(struct cache_entry *ce, int really)
{
struct stat st;
struct cache_entry *updated;
int changed, size;
if (lstat(ce->name, &st) < 0)
return ERR_PTR(-errno);
changed = ce_match_stat(ce, &st, really);
if (!changed) {
if (really && assume_unchanged &&
!(ce->ce_flags & htons(CE_VALID)))
; /* mark this one VALID again */
else
return NULL;
}
if (ce_modified(ce, &st, really))
return ERR_PTR(-EINVAL);
size = ce_size(ce);
updated = xmalloc(size);
memcpy(updated, ce, size);
fill_stat_cache_info(updated, &st);
/* In this case, if really is not set, we should leave
* CE_VALID bit alone. Otherwise, paths marked with
* --no-assume-unchanged (i.e. things to be edited) will
* reacquire CE_VALID bit automatically, which is not
* really what we want.
*/
if (!really && assume_unchanged && !(ce->ce_flags & htons(CE_VALID)))
updated->ce_flags &= ~htons(CE_VALID);
return updated;
}
static int refresh_cache(int really)
{
int i;
int has_errors = 0;
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce, *new;
ce = active_cache[i];
if (ce_stage(ce)) {
while ((i < active_nr) &&
! strcmp(active_cache[i]->name, ce->name))
i++;
i--;
if (allow_unmerged)
continue;
printf("%s: needs merge\n", ce->name);
has_errors = 1;
continue;
}
new = refresh_entry(ce, really);
if (!new)
continue;
if (IS_ERR(new)) {
if (not_new && PTR_ERR(new) == -ENOENT)
continue;
if (really && PTR_ERR(new) == -EINVAL) {
/* If we are doing --really-refresh that
* means the index is not valid anymore.
*/
ce->ce_flags &= ~htons(CE_VALID);
active_cache_changed = 1;
}
if (quiet)
continue;
printf("%s: needs update\n", ce->name);
has_errors = 1;
continue;
}
active_cache_changed = 1;
/* You can NOT just free active_cache[i] here, since it
* might not be necessarily malloc()ed but can also come
* from mmap(). */
active_cache[i] = new;
}
return has_errors;
}
/*
* We fundamentally don't like some paths: we don't want
* dot or dot-dot anywhere, and for obvious reasons don't
* want to recurse into ".git" either.
*
* Also, we don't want double slashes or slashes at the
* end that can make pathnames ambiguous.
*/
static int verify_dotfile(const char *rest)
{
/*
* The first character was '.', but that
* has already been discarded, we now test
* the rest.
*/
switch (*rest) {
/* "." is not allowed */
case '\0': case '/':
return 0;
/*
* ".git" followed by NUL or slash is bad. This
* shares the path end test with the ".." case.
*/
case 'g':
if (rest[1] != 'i')
break;
if (rest[2] != 't')
break;
rest += 2;
/* fallthrough */
case '.':
if (rest[1] == '\0' || rest[1] == '/')
return 0;
}
return 1;
}
static int verify_path(const char *path)
{
char c;
goto inside;
for (;;) {
if (!c)
return 1;
if (c == '/') {
inside:
c = *path++;
switch (c) {
default:
continue;
case '/': case '\0':
break;
case '.':
if (verify_dotfile(path))
continue;
}
return 0;
}
c = *path++;
}
}
static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
const char *path, int stage)
{
@ -653,6 +472,7 @@ int main(int argc, const char **argv)
const char *prefix = setup_git_directory();
int prefix_length = prefix ? strlen(prefix) : 0;
char set_executable_bit = 0;
unsigned int refresh_flags = 0;
git_config(git_default_config);
@ -673,7 +493,7 @@ int main(int argc, const char **argv)
continue;
}
if (!strcmp(path, "-q")) {
quiet = 1;
refresh_flags |= REFRESH_QUIET;
continue;
}
if (!strcmp(path, "--add")) {
@ -689,15 +509,15 @@ int main(int argc, const char **argv)
continue;
}
if (!strcmp(path, "--unmerged")) {
allow_unmerged = 1;
refresh_flags |= REFRESH_UNMERGED;
continue;
}
if (!strcmp(path, "--refresh")) {
has_errors |= refresh_cache(0);
has_errors |= refresh_cache(refresh_flags);
continue;
}
if (!strcmp(path, "--really-refresh")) {
has_errors |= refresh_cache(1);
has_errors |= refresh_cache(REFRESH_REALLY | refresh_flags);
continue;
}
if (!strcmp(path, "--cacheinfo")) {
@ -770,7 +590,7 @@ int main(int argc, const char **argv)
goto finish;
}
if (!strcmp(path, "--ignore-missing")) {
not_new = 1;
refresh_flags |= REFRESH_IGNORE_MISSING;
continue;
}
if (!strcmp(path, "--verbose")) {