зеркало из https://github.com/microsoft/git.git
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:
Коммит
a5c8a98ca7
|
@ -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
|
||||
'')
|
||||
;;
|
||||
|
|
89
Makefile
89
Makefile
|
@ -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
|
||||
|
|
|
@ -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"))
|
146
builtin-log.c
146
builtin-log.c
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
18
builtin.h
18
builtin.h
|
@ -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
|
||||
|
|
8
cache.h
8
cache.h
|
@ -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
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]))
|
||||
|
|
2
commit.h
2
commit.h
|
@ -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.
|
||||
|
|
|
@ -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
|
|
@ -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
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
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*);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
64
fetch-pack.c
64
fetch-pack.c
|
@ -262,22 +262,58 @@ static void mark_recent_complete_commits(unsigned long cutoff)
|
|||
|
||||
static void filter_refs(struct ref **refs, int nr_match, char **match)
|
||||
{
|
||||
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)
|
||||
|
|
7
fetch.c
7
fetch.c
|
@ -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);
|
||||
|
|
3
fetch.h
3
fetch.h
|
@ -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
|
||||
|
|
56
git-add.sh
56
git-add.sh
|
@ -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
|
38
git-am.sh
38
git-am.sh
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
70
git-rm.sh
70
git-rm.sh
|
@ -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
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;
|
||||
|
||||
|
|
51
log-tree.c
51
log-tree.c
|
@ -20,6 +20,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
|
|||
int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
|
||||
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
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;
|
||||
}
|
||||
|
|
183
read-cache.c
183
read-cache.c
|
@ -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;
|
||||
|
|
191
sha1_file.c
191
sha1_file.c
|
@ -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 && {
|
||||
|
|
192
update-index.c
192
update-index.c
|
@ -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")) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче