Merge part of 'lt/rev-list' into 'fk/blame'
Now blame will depend on the new revision walker infrastructure, we need to make it depend on earlier parts of Linus' rev-list topic branch, hence this merge. Signed-off-by: Junio C Hamano <junkio@cox.net>maint
commit
c40610422e
|
@ -2,6 +2,7 @@ GIT-VERSION-FILE
|
||||||
git
|
git
|
||||||
git-add
|
git-add
|
||||||
git-am
|
git-am
|
||||||
|
git-annotate
|
||||||
git-apply
|
git-apply
|
||||||
git-applymbox
|
git-applymbox
|
||||||
git-applypatch
|
git-applypatch
|
||||||
|
@ -22,6 +23,7 @@ git-convert-objects
|
||||||
git-count-objects
|
git-count-objects
|
||||||
git-cvsexportcommit
|
git-cvsexportcommit
|
||||||
git-cvsimport
|
git-cvsimport
|
||||||
|
git-cvsserver
|
||||||
git-daemon
|
git-daemon
|
||||||
git-diff
|
git-diff
|
||||||
git-diff-files
|
git-diff-files
|
||||||
|
@ -53,6 +55,7 @@ git-mailsplit
|
||||||
git-merge
|
git-merge
|
||||||
git-merge-base
|
git-merge-base
|
||||||
git-merge-index
|
git-merge-index
|
||||||
|
git-merge-tree
|
||||||
git-merge-octopus
|
git-merge-octopus
|
||||||
git-merge-one-file
|
git-merge-one-file
|
||||||
git-merge-ours
|
git-merge-ours
|
||||||
|
@ -60,6 +63,7 @@ git-merge-recursive
|
||||||
git-merge-resolve
|
git-merge-resolve
|
||||||
git-merge-stupid
|
git-merge-stupid
|
||||||
git-mktag
|
git-mktag
|
||||||
|
git-mktree
|
||||||
git-name-rev
|
git-name-rev
|
||||||
git-mv
|
git-mv
|
||||||
git-pack-redundant
|
git-pack-redundant
|
||||||
|
@ -84,6 +88,7 @@ git-resolve
|
||||||
git-rev-list
|
git-rev-list
|
||||||
git-rev-parse
|
git-rev-parse
|
||||||
git-revert
|
git-revert
|
||||||
|
git-rm
|
||||||
git-send-email
|
git-send-email
|
||||||
git-send-pack
|
git-send-pack
|
||||||
git-sh-setup
|
git-sh-setup
|
||||||
|
|
|
@ -7,7 +7,7 @@ git-add - Add files to the index file.
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
'git-add' [-n] [-v] <file>...
|
'git-add' [-n] [-v] [--] <file>...
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
@ -26,6 +26,11 @@ OPTIONS
|
||||||
-v::
|
-v::
|
||||||
Be verbose.
|
Be verbose.
|
||||||
|
|
||||||
|
--::
|
||||||
|
This option can be used to separate command-line options from
|
||||||
|
the list of files, (useful when filenames might be mistaken
|
||||||
|
for command-line options).
|
||||||
|
|
||||||
|
|
||||||
DISCUSSION
|
DISCUSSION
|
||||||
----------
|
----------
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
git-cvsserver(1)
|
||||||
|
================
|
||||||
|
|
||||||
|
NAME
|
||||||
|
----
|
||||||
|
git-cvsserver - A CVS server emulator for git
|
||||||
|
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
--------
|
||||||
|
[verse]
|
||||||
|
export CVS_SERVER=git-cvsserver
|
||||||
|
'cvs' -d :ext:user@server/path/repo.git co <HEAD_name>
|
||||||
|
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
-----------
|
||||||
|
|
||||||
|
This application is a CVS emulation layer for git.
|
||||||
|
|
||||||
|
It is highly functional. However, not all methods are implemented,
|
||||||
|
and for those methods that are implemented,
|
||||||
|
not all switches are implemented.
|
||||||
|
|
||||||
|
Testing has been done using both the CLI CVS client, and the Eclipse CVS
|
||||||
|
plugin. Most functionality works fine with both of these clients.
|
||||||
|
|
||||||
|
LIMITATIONS
|
||||||
|
-----------
|
||||||
|
Currently gitcvs only works over ssh connections.
|
||||||
|
|
||||||
|
|
||||||
|
INSTALLATION
|
||||||
|
------------
|
||||||
|
1. Put server.pl somewhere useful on the same machine that is hosting your git repos
|
||||||
|
|
||||||
|
2. For each repo that you want accessible from CVS you need to edit config in
|
||||||
|
the repo and add the following section.
|
||||||
|
|
||||||
|
[gitcvs]
|
||||||
|
enabled=1
|
||||||
|
logfile=/path/to/logfile
|
||||||
|
|
||||||
|
n.b. you need to ensure each user that is going to invoke server.pl has
|
||||||
|
write access to the log file.
|
||||||
|
|
||||||
|
5. On each client machine you need to set the following variables.
|
||||||
|
CVSROOT should be set as per normal, but the directory should point at the
|
||||||
|
appropriate git repo.
|
||||||
|
CVS_SERVER should be set to the server.pl script that has been put on the
|
||||||
|
remote machine.
|
||||||
|
|
||||||
|
6. Clients should now be able to check out modules (where modules are the names
|
||||||
|
of branches in git).
|
||||||
|
$ cvs co -d mylocaldir master
|
||||||
|
|
||||||
|
Operations supported
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
All the operations required for normal use are supported, including
|
||||||
|
checkout, diff, status, update, log, add, remove, commit.
|
||||||
|
Legacy monitoring operations are not supported (edit, watch and related).
|
||||||
|
Exports and tagging (tags and branches) are not supported at this stage.
|
||||||
|
|
||||||
|
The server will set the -k mode to binary when relevant. In proper GIT
|
||||||
|
tradition, the contents of the files are always respected.
|
||||||
|
No keyword expansion or newline munging is supported.
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
------------
|
||||||
|
|
||||||
|
git-cvsserver depends on DBD::SQLite.
|
||||||
|
|
||||||
|
Copyright and Authors
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
This program is copyright The Open University UK - 2006.
|
||||||
|
|
||||||
|
Authors: Martyn Smith <martyn@catalyst.net.nz>
|
||||||
|
Martin Langhoff <martin@catalyst.net.nz>
|
||||||
|
with ideas and patches from participants of the git-list <git@vger.kernel.org>.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
--------------
|
||||||
|
Documentation by Martyn Smith <martyn@catalyst.net.nz> and Martin Langhoff <martin@catalyst.net.nz>Matthias Urlichs <smurf@smurf.noris.de>.
|
||||||
|
|
||||||
|
GIT
|
||||||
|
---
|
||||||
|
Part of the gitlink:git[7] suite
|
|
@ -8,12 +8,14 @@ git-ls-files - Information about files in the index/working directory
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
'git-ls-files' [-z] [-t]
|
[verse]
|
||||||
|
'git-ls-files' [-z] [-t] [-v]
|
||||||
(--[cached|deleted|others|ignored|stage|unmerged|killed|modified])\*
|
(--[cached|deleted|others|ignored|stage|unmerged|killed|modified])\*
|
||||||
(-[c|d|o|i|s|u|k|m])\*
|
(-[c|d|o|i|s|u|k|m])\*
|
||||||
[-x <pattern>|--exclude=<pattern>]
|
[-x <pattern>|--exclude=<pattern>]
|
||||||
[-X <file>|--exclude-from=<file>]
|
[-X <file>|--exclude-from=<file>]
|
||||||
[--exclude-per-directory=<file>]
|
[--exclude-per-directory=<file>]
|
||||||
|
[--error-unmatch]
|
||||||
[--full-name] [--] [<file>]\*
|
[--full-name] [--] [<file>]\*
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
|
@ -72,6 +74,10 @@ OPTIONS
|
||||||
read additional exclude patterns that apply only to the
|
read additional exclude patterns that apply only to the
|
||||||
directory and its subdirectories in <file>.
|
directory and its subdirectories in <file>.
|
||||||
|
|
||||||
|
--error-unmatch::
|
||||||
|
If any <file> does not appear in the index, treat this as an
|
||||||
|
error (return 1).
|
||||||
|
|
||||||
-t::
|
-t::
|
||||||
Identify the file status with the following tags (followed by
|
Identify the file status with the following tags (followed by
|
||||||
a space) at the start of each line:
|
a space) at the start of each line:
|
||||||
|
@ -82,6 +88,10 @@ OPTIONS
|
||||||
K:: to be killed
|
K:: to be killed
|
||||||
?:: other
|
?:: other
|
||||||
|
|
||||||
|
-v::
|
||||||
|
Similar to `-t`, but use lowercase letters for files
|
||||||
|
that are marked as 'always matching index'.
|
||||||
|
|
||||||
--full-name::
|
--full-name::
|
||||||
When run from a subdirectory, the command usually
|
When run from a subdirectory, the command usually
|
||||||
outputs paths relative to the current directory. This
|
outputs paths relative to the current directory. This
|
||||||
|
|
|
@ -8,7 +8,10 @@ git-pack-objects - Create a packed archive of objects.
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
'git-pack-objects' [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list
|
[verse]
|
||||||
|
'git-pack-objects' [-q] [--no-reuse-delta] [--non-empty]
|
||||||
|
[--local] [--incremental] [--window=N] [--depth=N]
|
||||||
|
{--stdout | base-name} < object-list
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
|
@ -32,6 +35,10 @@ Placing both in the pack/ subdirectory of $GIT_OBJECT_DIRECTORY (or
|
||||||
any of the directories on $GIT_ALTERNATE_OBJECT_DIRECTORIES)
|
any of the directories on $GIT_ALTERNATE_OBJECT_DIRECTORIES)
|
||||||
enables git to read from such an archive.
|
enables git to read from such an archive.
|
||||||
|
|
||||||
|
In a packed archive, an object is either stored as a compressed
|
||||||
|
whole, or as a difference from some other object. The latter is
|
||||||
|
often called a delta.
|
||||||
|
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
-------
|
-------
|
||||||
|
@ -74,6 +81,18 @@ base-name::
|
||||||
Only create a packed archive if it would contain at
|
Only create a packed archive if it would contain at
|
||||||
least one object.
|
least one object.
|
||||||
|
|
||||||
|
-q::
|
||||||
|
This flag makes the command not to report its progress
|
||||||
|
on the standard error stream.
|
||||||
|
|
||||||
|
--no-reuse-delta::
|
||||||
|
When creating a packed archive in a repository that
|
||||||
|
has existing packs, the command reuses existing deltas.
|
||||||
|
This sometimes results in a slightly suboptimal pack.
|
||||||
|
This flag tells the command not to reuse existing deltas
|
||||||
|
but compute them from scratch.
|
||||||
|
|
||||||
|
|
||||||
Author
|
Author
|
||||||
------
|
------
|
||||||
Written by Linus Torvalds <torvalds@osdl.org>
|
Written by Linus Torvalds <torvalds@osdl.org>
|
||||||
|
|
|
@ -43,6 +43,12 @@ to fast forward the remote ref that matches <dst>. If
|
||||||
the optional plus `+` is used, the remote ref is updated
|
the optional plus `+` is used, the remote ref is updated
|
||||||
even if it does not result in a fast forward update.
|
even if it does not result in a fast forward update.
|
||||||
+
|
+
|
||||||
|
Note: If no explicit refspec is found, (that is neither
|
||||||
|
on the command line nor in any Push line of the
|
||||||
|
corresponding remotes file---see below), then all the
|
||||||
|
refs that exist both on the local side and on the remote
|
||||||
|
side are updated.
|
||||||
|
+
|
||||||
Some short-cut notations are also supported.
|
Some short-cut notations are also supported.
|
||||||
+
|
+
|
||||||
* `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
|
* `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
|
||||||
|
|
|
@ -7,14 +7,54 @@ git-rebase - Rebase local commits to new upstream head.
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
'git-rebase' <upstream> [<head>]
|
'git-rebase' [--onto <newbase>] <upstream> [<branch>]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
Rebases local commits to the new head of the upstream tree.
|
git-rebase applies to <upstream> (or optionally to <newbase>) commits
|
||||||
|
from <branch> that do not appear in <upstream>. When <branch> is not
|
||||||
|
specified it defaults to the current branch (HEAD).
|
||||||
|
|
||||||
|
When git-rebase is complete, <branch> will be updated to point to the
|
||||||
|
newly created line of commit objects, so the previous line will not be
|
||||||
|
accessible unless there are other references to it already.
|
||||||
|
|
||||||
|
Assume the following history exists and the current branch is "topic":
|
||||||
|
|
||||||
|
A---B---C topic
|
||||||
|
/
|
||||||
|
D---E---F---G master
|
||||||
|
|
||||||
|
From this point, the result of the following commands:
|
||||||
|
|
||||||
|
git-rebase master
|
||||||
|
git-rebase master topic
|
||||||
|
|
||||||
|
would be:
|
||||||
|
|
||||||
|
A'--B'--C' topic
|
||||||
|
/
|
||||||
|
D---E---F---G master
|
||||||
|
|
||||||
|
While, starting from the same point, the result of the following
|
||||||
|
commands:
|
||||||
|
|
||||||
|
git-rebase --onto master~1 master
|
||||||
|
git-rebase --onto master~1 master topic
|
||||||
|
|
||||||
|
would be:
|
||||||
|
|
||||||
|
A'--B'--C' topic
|
||||||
|
/
|
||||||
|
D---E---F---G master
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
-------
|
-------
|
||||||
|
<newbase>::
|
||||||
|
Starting point at which to create the new commits. If the
|
||||||
|
--onto option is not specified, the starting point is
|
||||||
|
<upstream>.
|
||||||
|
|
||||||
<upstream>::
|
<upstream>::
|
||||||
Upstream branch to compare against.
|
Upstream branch to compare against.
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ objects into pack files.
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
'git-repack' [-a] [-d] [-l] [-n]
|
'git-repack' [-a] [-d] [-f] [-l] [-n] [-q]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
@ -43,6 +43,14 @@ OPTIONS
|
||||||
Pass the `--local` option to `git pack-objects`, see
|
Pass the `--local` option to `git pack-objects`, see
|
||||||
gitlink:git-pack-objects[1].
|
gitlink:git-pack-objects[1].
|
||||||
|
|
||||||
|
-f::
|
||||||
|
Pass the `--no-reuse-delta` option to `git pack-objects`, see
|
||||||
|
gitlink:git-pack-objects[1].
|
||||||
|
|
||||||
|
-q::
|
||||||
|
Pass the `-q` option to `git pack-objects`, see
|
||||||
|
gitlink:git-pack-objects[1].
|
||||||
|
|
||||||
-n::
|
-n::
|
||||||
Do not update the server information with
|
Do not update the server information with
|
||||||
`git update-server-info`.
|
`git update-server-info`.
|
||||||
|
|
|
@ -16,7 +16,7 @@ SYNOPSIS
|
||||||
[ \--no-merges ]
|
[ \--no-merges ]
|
||||||
[ \--remove-empty ]
|
[ \--remove-empty ]
|
||||||
[ \--all ]
|
[ \--all ]
|
||||||
[ [ \--merge-order [ \--show-breaks ] ] | [ \--topo-order ] ]
|
[ \--topo-order ]
|
||||||
[ \--parents ]
|
[ \--parents ]
|
||||||
[ \--objects [ \--unpacked ] ]
|
[ \--objects [ \--unpacked ] ]
|
||||||
[ \--pretty | \--header ]
|
[ \--pretty | \--header ]
|
||||||
|
@ -94,57 +94,10 @@ OPTIONS
|
||||||
topological order (i.e. descendant commits are shown
|
topological order (i.e. descendant commits are shown
|
||||||
before their parents).
|
before their parents).
|
||||||
|
|
||||||
--merge-order::
|
|
||||||
When specified the commit history is decomposed into a unique
|
|
||||||
sequence of minimal, non-linear epochs and maximal, linear epochs.
|
|
||||||
Non-linear epochs are then linearised by sorting them into merge
|
|
||||||
order, which is described below.
|
|
||||||
+
|
|
||||||
Maximal, linear epochs correspond to periods of sequential development.
|
|
||||||
Minimal, non-linear epochs correspond to periods of divergent development
|
|
||||||
followed by a converging merge. The theory of epochs is described in more
|
|
||||||
detail at
|
|
||||||
link:http://blackcubes.dyndns.org/epoch/[http://blackcubes.dyndns.org/epoch/].
|
|
||||||
+
|
|
||||||
The merge order for a non-linear epoch is defined as a linearisation for which
|
|
||||||
the following invariants are true:
|
|
||||||
+
|
|
||||||
1. if a commit P is reachable from commit N, commit P sorts after commit N
|
|
||||||
in the linearised list.
|
|
||||||
2. if Pi and Pj are any two parents of a merge M (with i < j), then any
|
|
||||||
commit N, such that N is reachable from Pj but not reachable from Pi,
|
|
||||||
sorts before all commits reachable from Pi.
|
|
||||||
+
|
|
||||||
Invariant 1 states that later commits appear before earlier commits they are
|
|
||||||
derived from.
|
|
||||||
+
|
|
||||||
Invariant 2 states that commits unique to "later" parents in a merge, appear
|
|
||||||
before all commits from "earlier" parents of a merge.
|
|
||||||
|
|
||||||
--show-breaks::
|
|
||||||
Each item of the list is output with a 2-character prefix consisting
|
|
||||||
of one of: (|), (^), (=) followed by a space.
|
|
||||||
+
|
|
||||||
Commits marked with (=) represent the boundaries of minimal, non-linear epochs
|
|
||||||
and correspond either to the start of a period of divergent development or to
|
|
||||||
the end of such a period.
|
|
||||||
+
|
|
||||||
Commits marked with (|) are direct parents of commits immediately preceding
|
|
||||||
the marked commit in the list.
|
|
||||||
+
|
|
||||||
Commits marked with (^) are not parents of the immediately preceding commit.
|
|
||||||
These "breaks" represent necessary discontinuities implied by trying to
|
|
||||||
represent an arbitrary DAG in a linear form.
|
|
||||||
+
|
|
||||||
`--show-breaks` is only valid if `--merge-order` is also specified.
|
|
||||||
|
|
||||||
|
|
||||||
Author
|
Author
|
||||||
------
|
------
|
||||||
Written by Linus Torvalds <torvalds@osdl.org>
|
Written by Linus Torvalds <torvalds@osdl.org>
|
||||||
|
|
||||||
Original *--merge-order* logic by Jon Seymour <jon.seymour@gmail.com>
|
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
--------------
|
--------------
|
||||||
Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
|
Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
git-rm(1)
|
||||||
|
=========
|
||||||
|
|
||||||
|
NAME
|
||||||
|
----
|
||||||
|
git-rm - Remove files from the working tree and from the index.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
--------
|
||||||
|
'git-rm' [-f] [-n] [-v] [--] <file>...
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
-----------
|
||||||
|
A convenience wrapper for git-update-index --remove. For those coming
|
||||||
|
from cvs, git-rm provides an operation similar to "cvs rm" or "cvs
|
||||||
|
remove".
|
||||||
|
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-------
|
||||||
|
<file>...::
|
||||||
|
Files to remove from the index and optionally, from the
|
||||||
|
working tree as well.
|
||||||
|
|
||||||
|
-f::
|
||||||
|
Remove files from the working tree as well as from the index.
|
||||||
|
|
||||||
|
-n::
|
||||||
|
Don't actually remove the file(s), just show if they exist in
|
||||||
|
the index.
|
||||||
|
|
||||||
|
-v::
|
||||||
|
Be verbose.
|
||||||
|
|
||||||
|
--::
|
||||||
|
This option can be used to separate command-line options from
|
||||||
|
the list of files, (useful when filenames might be mistaken
|
||||||
|
for command-line options).
|
||||||
|
|
||||||
|
|
||||||
|
DISCUSSION
|
||||||
|
----------
|
||||||
|
|
||||||
|
The list of <file> given to the command is fed to `git-ls-files`
|
||||||
|
command to list files that are registered in the index and
|
||||||
|
are not ignored/excluded by `$GIT_DIR/info/exclude` file or
|
||||||
|
`.gitignore` file in each directory. This means two things:
|
||||||
|
|
||||||
|
. You can put the name of a directory on the command line, and the
|
||||||
|
command will remove all files in it and its subdirectories (the
|
||||||
|
directories themselves are never removed from the working tree);
|
||||||
|
|
||||||
|
. Giving the name of a file that is not in the index does not
|
||||||
|
remove that file.
|
||||||
|
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
--------
|
||||||
|
git-rm Documentation/\\*.txt::
|
||||||
|
|
||||||
|
Removes all `\*.txt` files from the index that are under the
|
||||||
|
`Documentation` directory and any of its subdirectories. The
|
||||||
|
files are not removed from the working tree.
|
||||||
|
+
|
||||||
|
Note that the asterisk `\*` is quoted from the shell in this
|
||||||
|
example; this lets the command include the files from
|
||||||
|
subdirectories of `Documentation/` directory.
|
||||||
|
|
||||||
|
git-rm -f git-*.sh::
|
||||||
|
|
||||||
|
Remove all git-*.sh scripts that are in the index. The files
|
||||||
|
are removed from the index, and (because of the -f option),
|
||||||
|
from the working tree as well. Because this example lets the
|
||||||
|
shell expand the asterisk (i.e. you are listing the files
|
||||||
|
explicitly), it does not remove `subdir/git-foo.sh`.
|
||||||
|
|
||||||
|
|
||||||
|
Author
|
||||||
|
------
|
||||||
|
Written by Linus Torvalds <torvalds@osdl.org>
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
--------------
|
||||||
|
Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
|
||||||
|
|
||||||
|
GIT
|
||||||
|
---
|
||||||
|
Part of the gitlink:git[7] suite
|
||||||
|
|
|
@ -8,11 +8,14 @@ git-update-index - Modifies the index or directory cache
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
|
[verse]
|
||||||
'git-update-index'
|
'git-update-index'
|
||||||
[--add] [--remove | --force-remove] [--replace]
|
[--add] [--remove | --force-remove] [--replace]
|
||||||
[--refresh [-q] [--unmerged] [--ignore-missing]]
|
[--refresh [-q] [--unmerged] [--ignore-missing]]
|
||||||
[--cacheinfo <mode> <object> <file>]\*
|
[--cacheinfo <mode> <object> <file>]\*
|
||||||
[--chmod=(+|-)x]
|
[--chmod=(+|-)x]
|
||||||
|
[--assume-unchanged | --no-assume-unchanged]
|
||||||
|
[--really-refresh]
|
||||||
[--info-only] [--index-info]
|
[--info-only] [--index-info]
|
||||||
[-z] [--stdin]
|
[-z] [--stdin]
|
||||||
[--verbose]
|
[--verbose]
|
||||||
|
@ -65,6 +68,18 @@ OPTIONS
|
||||||
--chmod=(+|-)x::
|
--chmod=(+|-)x::
|
||||||
Set the execute permissions on the updated files.
|
Set the execute permissions on the updated files.
|
||||||
|
|
||||||
|
--assume-unchanged, --no-assume-unchanged::
|
||||||
|
When these flags are specified, the object name recorded
|
||||||
|
for the paths are not updated. Instead, these options
|
||||||
|
sets and unsets the "assume unchanged" bit for the
|
||||||
|
paths. When the "assume unchanged" bit is on, git stops
|
||||||
|
checking the working tree files for possible
|
||||||
|
modifications, so you need to manually unset the bit to
|
||||||
|
tell git when you change the working tree file. This is
|
||||||
|
sometimes helpful when working with a big project on a
|
||||||
|
filesystem that has very slow lstat(2) system call
|
||||||
|
(e.g. cifs).
|
||||||
|
|
||||||
--info-only::
|
--info-only::
|
||||||
Do not create objects in the object database for all
|
Do not create objects in the object database for all
|
||||||
<file> arguments that follow this flag; just insert
|
<file> arguments that follow this flag; just insert
|
||||||
|
@ -193,6 +208,37 @@ $ git ls-files -s
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|
||||||
|
Using "assume unchanged" bit
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Many operations in git depend on your filesystem to have an
|
||||||
|
efficient `lstat(2)` implementation, so that `st_mtime`
|
||||||
|
information for working tree files can be cheaply checked to see
|
||||||
|
if the file contents have changed from the version recorded in
|
||||||
|
the index file. Unfortunately, some filesystems have
|
||||||
|
inefficient `lstat(2)`. If your filesystem is one of them, you
|
||||||
|
can set "assume unchanged" bit to paths you have not changed to
|
||||||
|
cause git not to do this check. Note that setting this bit on a
|
||||||
|
path does not mean git will check the contents of the file to
|
||||||
|
see if it has changed -- it makes git to omit any checking and
|
||||||
|
assume it has *not* changed. When you make changes to working
|
||||||
|
tree files, you have to explicitly tell git about it by dropping
|
||||||
|
"assume unchanged" bit, either before or after you modify them.
|
||||||
|
|
||||||
|
In order to set "assume unchanged" bit, use `--assume-unchanged`
|
||||||
|
option. To unset, use `--no-assume-unchanged`.
|
||||||
|
|
||||||
|
The command looks at `core.ignorestat` configuration variable. When
|
||||||
|
this is true, paths updated with `git-update-index paths...` and
|
||||||
|
paths updated with other git commands that update both index and
|
||||||
|
working tree (e.g. `git-apply --index`, `git-checkout-index -u`,
|
||||||
|
and `git-read-tree -u`) are automatically marked as "assume
|
||||||
|
unchanged". Note that "assume unchanged" bit is *not* set if
|
||||||
|
`git-update-index --refresh` finds the working tree file matches
|
||||||
|
the index (use `git-update-index --really-refresh` if you want
|
||||||
|
to mark them as "assume unchanged").
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
To update and refresh only the files already checked out:
|
To update and refresh only the files already checked out:
|
||||||
|
@ -201,6 +247,35 @@ To update and refresh only the files already checked out:
|
||||||
$ git-checkout-index -n -f -a && git-update-index --ignore-missing --refresh
|
$ git-checkout-index -n -f -a && git-update-index --ignore-missing --refresh
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
|
On an inefficient filesystem with `core.ignorestat` set:
|
||||||
|
|
||||||
|
------------
|
||||||
|
$ git update-index --really-refresh <1>
|
||||||
|
$ git update-index --no-assume-unchanged foo.c <2>
|
||||||
|
$ git diff --name-only <3>
|
||||||
|
$ edit foo.c
|
||||||
|
$ git diff --name-only <4>
|
||||||
|
M foo.c
|
||||||
|
$ git update-index foo.c <5>
|
||||||
|
$ git diff --name-only <6>
|
||||||
|
$ edit foo.c
|
||||||
|
$ git diff --name-only <7>
|
||||||
|
$ git update-index --no-assume-unchanged foo.c <8>
|
||||||
|
$ git diff --name-only <9>
|
||||||
|
M foo.c
|
||||||
|
|
||||||
|
<1> forces lstat(2) to set "assume unchanged" bits for paths
|
||||||
|
that match index.
|
||||||
|
<2> mark the path to be edited.
|
||||||
|
<3> this does lstat(2) and finds index matches the path.
|
||||||
|
<4> this does lstat(2) and finds index does not match the path.
|
||||||
|
<5> registering the new version to index sets "assume unchanged" bit.
|
||||||
|
<6> and it is assumed unchanged.
|
||||||
|
<7> even after you edit it.
|
||||||
|
<8> you can tell about the change after the fact.
|
||||||
|
<9> now it checks with lstat(2) and finds it has been changed.
|
||||||
|
------------
|
||||||
|
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
-------------
|
-------------
|
||||||
|
@ -213,6 +288,9 @@ in the index and the file mode on the filesystem if they differ only on
|
||||||
executable bit. On such an unfortunate filesystem, you may
|
executable bit. On such an unfortunate filesystem, you may
|
||||||
need to use `git-update-index --chmod=`.
|
need to use `git-update-index --chmod=`.
|
||||||
|
|
||||||
|
The command looks at `core.ignorestat` configuration variable. See
|
||||||
|
'Using "assume unchanged" bit' section above.
|
||||||
|
|
||||||
|
|
||||||
See Also
|
See Also
|
||||||
--------
|
--------
|
||||||
|
|
4
INSTALL
4
INSTALL
|
@ -40,9 +40,7 @@ Issues of note:
|
||||||
|
|
||||||
If you don't have openssl, you can use one of the SHA1 libraries
|
If you don't have openssl, you can use one of the SHA1 libraries
|
||||||
that come with git (git includes the one from Mozilla, and has
|
that come with git (git includes the one from Mozilla, and has
|
||||||
its own PowerPC-optimized one too - see the Makefile), and you
|
its own PowerPC and ARM optimized ones too - see the Makefile).
|
||||||
can avoid the bignum support by excising git-rev-list support
|
|
||||||
for "--merge-order" (by hand).
|
|
||||||
|
|
||||||
- "libcurl" and "curl" executable. git-http-fetch and
|
- "libcurl" and "curl" executable. git-http-fetch and
|
||||||
git-fetch use them. If you do not use http
|
git-fetch use them. If you do not use http
|
||||||
|
|
110
Makefile
110
Makefile
|
@ -6,8 +6,8 @@ all:
|
||||||
# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
|
# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
|
||||||
# choice) has very fast version optimized for i586.
|
# choice) has very fast version optimized for i586.
|
||||||
#
|
#
|
||||||
# Define NO_OPENSSL environment variable if you do not have OpenSSL. You will
|
# Define NO_OPENSSL environment variable if you do not have OpenSSL.
|
||||||
# miss out git-rev-list --merge-order. This also implies MOZILLA_SHA1.
|
# This also implies MOZILLA_SHA1.
|
||||||
#
|
#
|
||||||
# Define NO_CURL if you do not have curl installed. git-http-pull and
|
# Define NO_CURL if you do not have curl installed. git-http-pull and
|
||||||
# git-http-push are not built, and you cannot use http:// and https://
|
# git-http-push are not built, and you cannot use http:// and https://
|
||||||
|
@ -77,6 +77,12 @@ GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
|
||||||
@$(SHELL_PATH) ./GIT-VERSION-GEN
|
@$(SHELL_PATH) ./GIT-VERSION-GEN
|
||||||
-include GIT-VERSION-FILE
|
-include GIT-VERSION-FILE
|
||||||
|
|
||||||
|
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
||||||
|
uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
|
||||||
|
uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not')
|
||||||
|
uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
|
||||||
|
uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')
|
||||||
|
|
||||||
# CFLAGS and LDFLAGS are for the users to override from the command line.
|
# CFLAGS and LDFLAGS are for the users to override from the command line.
|
||||||
|
|
||||||
CFLAGS = -g -O2 -Wall
|
CFLAGS = -g -O2 -Wall
|
||||||
|
@ -87,7 +93,7 @@ STRIP ?= strip
|
||||||
|
|
||||||
prefix = $(HOME)
|
prefix = $(HOME)
|
||||||
bindir = $(prefix)/bin
|
bindir = $(prefix)/bin
|
||||||
gitexecdir = $(prefix)/bin
|
gitexecdir = $(bindir)
|
||||||
template_dir = $(prefix)/share/git-core/templates/
|
template_dir = $(prefix)/share/git-core/templates/
|
||||||
GIT_PYTHON_DIR = $(prefix)/share/git-core/python
|
GIT_PYTHON_DIR = $(prefix)/share/git-core/python
|
||||||
# DESTDIR=
|
# DESTDIR=
|
||||||
|
@ -114,7 +120,7 @@ SCRIPT_SH = \
|
||||||
git-merge-one-file.sh git-parse-remote.sh \
|
git-merge-one-file.sh git-parse-remote.sh \
|
||||||
git-prune.sh git-pull.sh git-push.sh git-rebase.sh \
|
git-prune.sh git-pull.sh git-push.sh git-rebase.sh \
|
||||||
git-repack.sh git-request-pull.sh git-reset.sh \
|
git-repack.sh git-request-pull.sh git-reset.sh \
|
||||||
git-resolve.sh git-revert.sh git-sh-setup.sh \
|
git-resolve.sh git-revert.sh git-rm.sh git-sh-setup.sh \
|
||||||
git-tag.sh git-verify-tag.sh git-whatchanged.sh \
|
git-tag.sh git-verify-tag.sh git-whatchanged.sh \
|
||||||
git-applymbox.sh git-applypatch.sh git-am.sh \
|
git-applymbox.sh git-applypatch.sh git-am.sh \
|
||||||
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
|
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
|
||||||
|
@ -124,6 +130,7 @@ SCRIPT_SH = \
|
||||||
SCRIPT_PERL = \
|
SCRIPT_PERL = \
|
||||||
git-archimport.perl git-cvsimport.perl git-relink.perl \
|
git-archimport.perl git-cvsimport.perl git-relink.perl \
|
||||||
git-shortlog.perl git-fmt-merge-msg.perl git-rerere.perl \
|
git-shortlog.perl git-fmt-merge-msg.perl git-rerere.perl \
|
||||||
|
git-annotate.perl git-cvsserver.perl \
|
||||||
git-svnimport.perl git-mv.perl git-cvsexportcommit.perl
|
git-svnimport.perl git-mv.perl git-cvsexportcommit.perl
|
||||||
|
|
||||||
SCRIPT_PYTHON = \
|
SCRIPT_PYTHON = \
|
||||||
|
@ -136,7 +143,7 @@ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
|
||||||
|
|
||||||
# The ones that do not have to link with lcrypto nor lz.
|
# The ones that do not have to link with lcrypto nor lz.
|
||||||
SIMPLE_PROGRAMS = \
|
SIMPLE_PROGRAMS = \
|
||||||
git-get-tar-commit-id$X git-mailinfo$X git-mailsplit$X \
|
git-get-tar-commit-id$X git-mailsplit$X \
|
||||||
git-stripspace$X git-daemon$X
|
git-stripspace$X git-daemon$X
|
||||||
|
|
||||||
# ... and all the rest that could be moved out of bindir to gitexecdir
|
# ... and all the rest that could be moved out of bindir to gitexecdir
|
||||||
|
@ -146,8 +153,8 @@ PROGRAMS = \
|
||||||
git-convert-objects$X git-diff-files$X \
|
git-convert-objects$X git-diff-files$X \
|
||||||
git-diff-index$X git-diff-stages$X \
|
git-diff-index$X git-diff-stages$X \
|
||||||
git-diff-tree$X git-fetch-pack$X git-fsck-objects$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-hash-object$X git-index-pack$X git-init-db$X git-local-fetch$X \
|
||||||
git-local-fetch$X git-ls-files$X git-ls-tree$X git-merge-base$X \
|
git-ls-files$X git-ls-tree$X git-mailinfo$X git-merge-base$X \
|
||||||
git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \
|
git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \
|
||||||
git-peek-remote$X git-prune-packed$X git-read-tree$X \
|
git-peek-remote$X git-prune-packed$X git-read-tree$X \
|
||||||
git-receive-pack$X git-rev-list$X git-rev-parse$X \
|
git-receive-pack$X git-rev-list$X git-rev-parse$X \
|
||||||
|
@ -184,8 +191,8 @@ LIB_FILE=libgit.a
|
||||||
|
|
||||||
LIB_H = \
|
LIB_H = \
|
||||||
blob.h cache.h commit.h count-delta.h csum-file.h delta.h \
|
blob.h cache.h commit.h count-delta.h csum-file.h delta.h \
|
||||||
diff.h epoch.h object.h pack.h pkt-line.h quote.h refs.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
|
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h
|
||||||
|
|
||||||
DIFF_OBJS = \
|
DIFF_OBJS = \
|
||||||
diff.o diffcore-break.o diffcore-order.o diffcore-pathspec.o \
|
diff.o diffcore-break.o diffcore-order.o diffcore-pathspec.o \
|
||||||
|
@ -198,18 +205,12 @@ LIB_OBJS = \
|
||||||
quote.o read-cache.o refs.o run-command.o \
|
quote.o read-cache.o refs.o run-command.o \
|
||||||
server-info.o setup.o sha1_file.o sha1_name.o strbuf.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 \
|
tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
|
||||||
fetch-clone.o \
|
fetch-clone.o revision.o pager.o \
|
||||||
$(DIFF_OBJS)
|
$(DIFF_OBJS)
|
||||||
|
|
||||||
LIBS = $(LIB_FILE)
|
LIBS = $(LIB_FILE)
|
||||||
LIBS += -lz
|
LIBS += -lz
|
||||||
|
|
||||||
# Shell quote;
|
|
||||||
# Result of this needs to be placed inside ''
|
|
||||||
shq = $(subst ','\'',$(1))
|
|
||||||
# This has surrounding ''
|
|
||||||
shellquote = '$(call shq,$(1))'
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Platform specific tweaks
|
# Platform specific tweaks
|
||||||
#
|
#
|
||||||
|
@ -217,10 +218,6 @@ shellquote = '$(call shq,$(1))'
|
||||||
# We choose to avoid "if .. else if .. else .. endif endif"
|
# We choose to avoid "if .. else if .. else .. endif endif"
|
||||||
# because maintaining the nesting to match is a pain. If
|
# because maintaining the nesting to match is a pain. If
|
||||||
# we had "elif" things would have been much nicer...
|
# we had "elif" things would have been much nicer...
|
||||||
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
|
||||||
uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
|
|
||||||
uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not')
|
|
||||||
uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
|
|
||||||
|
|
||||||
ifeq ($(uname_S),Darwin)
|
ifeq ($(uname_S),Darwin)
|
||||||
NEEDS_SSL_WITH_CRYPTO = YesPlease
|
NEEDS_SSL_WITH_CRYPTO = YesPlease
|
||||||
|
@ -235,10 +232,10 @@ endif
|
||||||
ifeq ($(uname_S),SunOS)
|
ifeq ($(uname_S),SunOS)
|
||||||
NEEDS_SOCKET = YesPlease
|
NEEDS_SOCKET = YesPlease
|
||||||
NEEDS_NSL = YesPlease
|
NEEDS_NSL = YesPlease
|
||||||
NEEDS_LIBICONV = YesPlease
|
|
||||||
SHELL_PATH = /bin/bash
|
SHELL_PATH = /bin/bash
|
||||||
NO_STRCASESTR = YesPlease
|
NO_STRCASESTR = YesPlease
|
||||||
ifeq ($(uname_R),5.8)
|
ifeq ($(uname_R),5.8)
|
||||||
|
NEEDS_LIBICONV = YesPlease
|
||||||
NO_UNSETENV = YesPlease
|
NO_UNSETENV = YesPlease
|
||||||
NO_SETENV = YesPlease
|
NO_SETENV = YesPlease
|
||||||
endif
|
endif
|
||||||
|
@ -297,8 +294,10 @@ endif
|
||||||
ifdef WITH_OWN_SUBPROCESS_PY
|
ifdef WITH_OWN_SUBPROCESS_PY
|
||||||
PYMODULES += compat/subprocess.py
|
PYMODULES += compat/subprocess.py
|
||||||
else
|
else
|
||||||
ifneq ($(shell $(PYTHON_PATH) -c 'import subprocess;print"OK"' 2>/dev/null),OK)
|
ifeq ($(NO_PYTHON),)
|
||||||
PYMODULES += compat/subprocess.py
|
ifneq ($(shell $(PYTHON_PATH) -c 'import subprocess;print"OK"' 2>/dev/null),OK)
|
||||||
|
PYMODULES += compat/subprocess.py
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -325,7 +324,6 @@ ifndef NO_CURL
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifndef NO_OPENSSL
|
ifndef NO_OPENSSL
|
||||||
LIB_OBJS += epoch.o
|
|
||||||
OPENSSL_LIBSSL = -lssl
|
OPENSSL_LIBSSL = -lssl
|
||||||
ifdef OPENSSLDIR
|
ifdef OPENSSLDIR
|
||||||
# Again this may be problematic -- gcc does not always want -R.
|
# Again this may be problematic -- gcc does not always want -R.
|
||||||
|
@ -422,7 +420,21 @@ ifdef NO_ACCURATE_DIFF
|
||||||
ALL_CFLAGS += -DNO_ACCURATE_DIFF
|
ALL_CFLAGS += -DNO_ACCURATE_DIFF
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ALL_CFLAGS += -DSHA1_HEADER=$(call shellquote,$(SHA1_HEADER)) $(COMPAT_CFLAGS)
|
# Shell quote (do not use $(call) to accomodate ancient setups);
|
||||||
|
|
||||||
|
SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
|
||||||
|
|
||||||
|
DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
|
||||||
|
bindir_SQ = $(subst ','\'',$(bindir))
|
||||||
|
gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
|
||||||
|
template_dir_SQ = $(subst ','\'',$(template_dir))
|
||||||
|
|
||||||
|
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
|
||||||
|
PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
|
||||||
|
PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
|
||||||
|
GIT_PYTHON_DIR_SQ = $(subst ','\'',$(GIT_PYTHON_DIR))
|
||||||
|
|
||||||
|
ALL_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' $(COMPAT_CFLAGS)
|
||||||
LIB_OBJS += $(COMPAT_OBJS)
|
LIB_OBJS += $(COMPAT_OBJS)
|
||||||
export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
|
export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
|
||||||
### Build rules
|
### Build rules
|
||||||
|
@ -437,11 +449,11 @@ strip: $(PROGRAMS) git$X
|
||||||
|
|
||||||
git$X: git.c $(LIB_FILE)
|
git$X: git.c $(LIB_FILE)
|
||||||
$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
|
$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
|
||||||
$(CFLAGS) $(COMPAT_CFLAGS) -o $@ $(filter %.c,$^) $(LIB_FILE)
|
$(ALL_CFLAGS) -o $@ $(filter %.c,$^) $(LIB_FILE) $(LIBS)
|
||||||
|
|
||||||
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
|
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
|
||||||
rm -f $@
|
rm -f $@
|
||||||
sed -e '1s|#!.*/sh|#!$(call shq,$(SHELL_PATH))|' \
|
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
|
||||||
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
|
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
|
||||||
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
|
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
|
||||||
-e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
|
-e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
|
||||||
|
@ -450,15 +462,15 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
|
||||||
|
|
||||||
$(patsubst %.perl,%,$(SCRIPT_PERL)) : % : %.perl
|
$(patsubst %.perl,%,$(SCRIPT_PERL)) : % : %.perl
|
||||||
rm -f $@
|
rm -f $@
|
||||||
sed -e '1s|#!.*perl|#!$(call shq,$(PERL_PATH))|' \
|
sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
|
||||||
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
|
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
|
||||||
$@.perl >$@
|
$@.perl >$@
|
||||||
chmod +x $@
|
chmod +x $@
|
||||||
|
|
||||||
$(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
|
$(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
|
||||||
rm -f $@
|
rm -f $@
|
||||||
sed -e '1s|#!.*python|#!$(call shq,$(PYTHON_PATH))|' \
|
sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
|
||||||
-e 's|@@GIT_PYTHON_PATH@@|$(call shq,$(GIT_PYTHON_DIR))|g' \
|
-e 's|@@GIT_PYTHON_PATH@@|$(GIT_PYTHON_DIR_SQ)|g' \
|
||||||
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
|
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
|
||||||
$@.py >$@
|
$@.py >$@
|
||||||
chmod +x $@
|
chmod +x $@
|
||||||
|
@ -484,32 +496,42 @@ git$X git.spec \
|
||||||
%.o: %.S
|
%.o: %.S
|
||||||
$(CC) -o $*.o -c $(ALL_CFLAGS) $<
|
$(CC) -o $*.o -c $(ALL_CFLAGS) $<
|
||||||
|
|
||||||
exec_cmd.o: ALL_CFLAGS += -DGIT_EXEC_PATH=\"$(gitexecdir)\"
|
exec_cmd.o: exec_cmd.c
|
||||||
|
$(CC) -o $*.o -c $(ALL_CFLAGS) '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' $<
|
||||||
|
|
||||||
git-%$X: %.o $(LIB_FILE)
|
git-%$X: %.o $(LIB_FILE)
|
||||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
|
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
|
||||||
|
|
||||||
git-mailinfo$X : SIMPLE_LIB += $(LIB_4_ICONV)
|
|
||||||
$(SIMPLE_PROGRAMS) : $(LIB_FILE)
|
$(SIMPLE_PROGRAMS) : $(LIB_FILE)
|
||||||
$(SIMPLE_PROGRAMS) : git-%$X : %.o
|
$(SIMPLE_PROGRAMS) : git-%$X : %.o
|
||||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
||||||
$(LIB_FILE) $(SIMPLE_LIB)
|
$(LIB_FILE) $(SIMPLE_LIB)
|
||||||
|
|
||||||
git-http-fetch$X: fetch.o http.o
|
git-mailinfo$X: mailinfo.o $(LIB_FILE)
|
||||||
git-http-push$X: http.o
|
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
||||||
|
$(LIB_FILE) $(SIMPLE_LIB) $(LIB_4_ICONV)
|
||||||
|
|
||||||
git-local-fetch$X: fetch.o
|
git-local-fetch$X: fetch.o
|
||||||
git-ssh-fetch$X: rsh.o fetch.o
|
git-ssh-fetch$X: rsh.o fetch.o
|
||||||
git-ssh-upload$X: rsh.o
|
git-ssh-upload$X: rsh.o
|
||||||
git-ssh-pull$X: rsh.o fetch.o
|
git-ssh-pull$X: rsh.o fetch.o
|
||||||
git-ssh-push$X: rsh.o
|
git-ssh-push$X: rsh.o
|
||||||
|
|
||||||
git-http-fetch$X: LIBS += $(CURL_LIBCURL)
|
git-http-fetch$X: fetch.o http.o http-fetch.o $(LIB_FILE)
|
||||||
git-http-push$X: LIBS += $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
|
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
||||||
git-rev-list$X: LIBS += $(OPENSSL_LIBSSL)
|
$(LIBS) $(CURL_LIBCURL)
|
||||||
|
|
||||||
|
git-http-push$X: http.o http-push.o $(LIB_FILE)
|
||||||
|
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
||||||
|
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
|
||||||
|
|
||||||
|
git-rev-list$X: rev-list.o $(LIB_FILE)
|
||||||
|
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
||||||
|
$(LIBS) $(OPENSSL_LIBSSL)
|
||||||
|
|
||||||
init-db.o: init-db.c
|
init-db.o: init-db.c
|
||||||
$(CC) -c $(ALL_CFLAGS) \
|
$(CC) -c $(ALL_CFLAGS) \
|
||||||
-DDEFAULT_GIT_TEMPLATE_DIR=$(call shellquote,"$(template_dir)") $*.c
|
-DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $*.c
|
||||||
|
|
||||||
$(LIB_OBJS): $(LIB_H)
|
$(LIB_OBJS): $(LIB_H)
|
||||||
$(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H)
|
$(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H)
|
||||||
|
@ -547,13 +569,13 @@ check:
|
||||||
### Installation rules
|
### Installation rules
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
$(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(bindir))
|
$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(bindir_SQ)'
|
||||||
$(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(gitexecdir))
|
$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(gitexecdir_SQ)'
|
||||||
$(INSTALL) $(ALL_PROGRAMS) $(call shellquote,$(DESTDIR)$(gitexecdir))
|
$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
|
||||||
$(INSTALL) git$X gitk $(call shellquote,$(DESTDIR)$(bindir))
|
$(INSTALL) git$X gitk '$(DESTDIR_SQ)$(bindir_SQ)'
|
||||||
$(MAKE) -C templates install
|
$(MAKE) -C templates install
|
||||||
$(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR))
|
$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
|
||||||
$(INSTALL) $(PYMODULES) $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR))
|
$(INSTALL) $(PYMODULES) '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
|
||||||
|
|
||||||
install-doc:
|
install-doc:
|
||||||
$(MAKE) -C Documentation install
|
$(MAKE) -C Documentation install
|
||||||
|
|
2
apply.c
2
apply.c
|
@ -1317,7 +1317,7 @@ static int check_patch(struct patch *patch)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
changed = ce_match_stat(active_cache[pos], &st);
|
changed = ce_match_stat(active_cache[pos], &st, 1);
|
||||||
if (changed)
|
if (changed)
|
||||||
return error("%s: does not match index",
|
return error("%s: does not match index",
|
||||||
old_name);
|
old_name);
|
||||||
|
|
1
blame.c
1
blame.c
|
@ -6,7 +6,6 @@
|
||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
#include "epoch.h"
|
|
||||||
#include "diff.h"
|
#include "diff.h"
|
||||||
|
|
||||||
#define DEBUG 0
|
#define DEBUG 0
|
||||||
|
|
11
cache.h
11
cache.h
|
@ -10,7 +10,7 @@
|
||||||
#define deflateBound(c,s) ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
|
#define deflateBound(c,s) ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(DT_UNKNOWN) && !NO_D_TYPE_IN_DIRENT
|
#if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
|
||||||
#define DTYPE(de) ((de)->d_type)
|
#define DTYPE(de) ((de)->d_type)
|
||||||
#else
|
#else
|
||||||
#undef DT_UNKNOWN
|
#undef DT_UNKNOWN
|
||||||
|
@ -91,6 +91,7 @@ struct cache_entry {
|
||||||
#define CE_NAMEMASK (0x0fff)
|
#define CE_NAMEMASK (0x0fff)
|
||||||
#define CE_STAGEMASK (0x3000)
|
#define CE_STAGEMASK (0x3000)
|
||||||
#define CE_UPDATE (0x4000)
|
#define CE_UPDATE (0x4000)
|
||||||
|
#define CE_VALID (0x8000)
|
||||||
#define CE_STAGESHIFT 12
|
#define CE_STAGESHIFT 12
|
||||||
|
|
||||||
#define create_ce_flags(len, stage) htons((len) | ((stage) << CE_STAGESHIFT))
|
#define create_ce_flags(len, stage) htons((len) | ((stage) << CE_STAGESHIFT))
|
||||||
|
@ -144,8 +145,8 @@ extern int add_cache_entry(struct cache_entry *ce, int option);
|
||||||
extern int remove_cache_entry_at(int pos);
|
extern int remove_cache_entry_at(int pos);
|
||||||
extern int remove_file_from_cache(const char *path);
|
extern int remove_file_from_cache(const char *path);
|
||||||
extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
|
extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
|
||||||
extern int ce_match_stat(struct cache_entry *ce, struct stat *st);
|
extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int);
|
||||||
extern int ce_modified(struct cache_entry *ce, struct stat *st);
|
extern int ce_modified(struct cache_entry *ce, struct stat *st, int);
|
||||||
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
|
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
|
||||||
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type);
|
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type);
|
||||||
extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
|
extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
|
||||||
|
@ -161,6 +162,7 @@ extern int commit_index_file(struct cache_file *);
|
||||||
extern void rollback_index_file(struct cache_file *);
|
extern void rollback_index_file(struct cache_file *);
|
||||||
|
|
||||||
extern int trust_executable_bit;
|
extern int trust_executable_bit;
|
||||||
|
extern int assume_unchanged;
|
||||||
extern int only_use_symrefs;
|
extern int only_use_symrefs;
|
||||||
extern int diff_rename_limit_default;
|
extern int diff_rename_limit_default;
|
||||||
extern int shared_repository;
|
extern int shared_repository;
|
||||||
|
@ -350,4 +352,7 @@ extern int copy_fd(int ifd, int ofd);
|
||||||
extern int receive_unpack_pack(int fd[2], const char *me, int quiet);
|
extern int receive_unpack_pack(int fd[2], const char *me, int quiet);
|
||||||
extern int receive_keep_pack(int fd[2], const char *me, int quiet);
|
extern int receive_keep_pack(int fd[2], const char *me, int quiet);
|
||||||
|
|
||||||
|
/* pager.c */
|
||||||
|
extern void setup_pager(void);
|
||||||
|
|
||||||
#endif /* CACHE_H */
|
#endif /* CACHE_H */
|
||||||
|
|
|
@ -116,6 +116,7 @@ int main(int argc, char **argv)
|
||||||
int all = 0;
|
int all = 0;
|
||||||
|
|
||||||
prefix = setup_git_directory();
|
prefix = setup_git_directory();
|
||||||
|
git_config(git_default_config);
|
||||||
prefix_length = prefix ? strlen(prefix) : 0;
|
prefix_length = prefix ? strlen(prefix) : 0;
|
||||||
|
|
||||||
if (read_cache() < 0) {
|
if (read_cache() < 0) {
|
||||||
|
|
3
commit.c
3
commit.c
|
@ -212,7 +212,8 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
|
||||||
if (memcmp(bufptr, "tree ", 5))
|
if (memcmp(bufptr, "tree ", 5))
|
||||||
return error("bogus commit object %s", sha1_to_hex(item->object.sha1));
|
return error("bogus commit object %s", sha1_to_hex(item->object.sha1));
|
||||||
if (get_sha1_hex(bufptr + 5, parent) < 0)
|
if (get_sha1_hex(bufptr + 5, parent) < 0)
|
||||||
return error("bad tree pointer in commit %s\n", sha1_to_hex(item->object.sha1));
|
return error("bad tree pointer in commit %s",
|
||||||
|
sha1_to_hex(item->object.sha1));
|
||||||
item->tree = lookup_tree(parent);
|
item->tree = lookup_tree(parent);
|
||||||
if (item->tree)
|
if (item->tree)
|
||||||
n_refs++;
|
n_refs++;
|
||||||
|
|
5
config.c
5
config.c
|
@ -222,6 +222,11 @@ int git_default_config(const char *var, const char *value)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strcmp(var, "core.ignorestat")) {
|
||||||
|
assume_unchanged = git_config_bool(var, value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!strcmp(var, "core.symrefsonly")) {
|
if (!strcmp(var, "core.symrefsonly")) {
|
||||||
only_use_symrefs = git_config_bool(var, value);
|
only_use_symrefs = git_config_bool(var, value);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -8,7 +8,7 @@ use vars qw/ $AUTHOR $VERSION
|
||||||
$GIT_SVN_INDEX $GIT_SVN
|
$GIT_SVN_INDEX $GIT_SVN
|
||||||
$GIT_DIR $REV_DIR/;
|
$GIT_DIR $REV_DIR/;
|
||||||
$AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
|
$AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
|
||||||
$VERSION = '0.9.1';
|
$VERSION = '0.10.0';
|
||||||
$GIT_DIR = $ENV{GIT_DIR} || "$ENV{PWD}/.git";
|
$GIT_DIR = $ENV{GIT_DIR} || "$ENV{PWD}/.git";
|
||||||
$GIT_SVN = $ENV{GIT_SVN_ID} || 'git-svn';
|
$GIT_SVN = $ENV{GIT_SVN_ID} || 'git-svn';
|
||||||
$GIT_SVN_INDEX = "$GIT_DIR/$GIT_SVN/index";
|
$GIT_SVN_INDEX = "$GIT_DIR/$GIT_SVN/index";
|
||||||
|
@ -30,6 +30,7 @@ use File::Basename qw/dirname basename/;
|
||||||
use File::Path qw/mkpath/;
|
use File::Path qw/mkpath/;
|
||||||
use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/;
|
use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/;
|
||||||
use File::Spec qw//;
|
use File::Spec qw//;
|
||||||
|
use POSIX qw/strftime/;
|
||||||
my $sha1 = qr/[a-f\d]{40}/;
|
my $sha1 = qr/[a-f\d]{40}/;
|
||||||
my $sha1_short = qr/[a-f\d]{6,40}/;
|
my $sha1_short = qr/[a-f\d]{6,40}/;
|
||||||
my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
|
my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
|
||||||
|
@ -49,6 +50,7 @@ my %cmd = (
|
||||||
fetch => [ \&fetch, "Download new revisions from SVN" ],
|
fetch => [ \&fetch, "Download new revisions from SVN" ],
|
||||||
init => [ \&init, "Initialize and fetch (import)"],
|
init => [ \&init, "Initialize and fetch (import)"],
|
||||||
commit => [ \&commit, "Commit git revisions to SVN" ],
|
commit => [ \&commit, "Commit git revisions to SVN" ],
|
||||||
|
'show-ignore' => [ \&show_ignore, "Show svn:ignore listings" ],
|
||||||
rebuild => [ \&rebuild, "Rebuild git-svn metadata (after git clone)" ],
|
rebuild => [ \&rebuild, "Rebuild git-svn metadata (after git clone)" ],
|
||||||
help => [ \&usage, "Show help" ],
|
help => [ \&usage, "Show help" ],
|
||||||
);
|
);
|
||||||
|
@ -258,6 +260,30 @@ sub commit {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub show_ignore {
|
||||||
|
require File::Find or die $!;
|
||||||
|
my $exclude_file = "$GIT_DIR/info/exclude";
|
||||||
|
open my $fh, '<', $exclude_file or croak $!;
|
||||||
|
chomp(my @excludes = (<$fh>));
|
||||||
|
close $fh or croak $!;
|
||||||
|
|
||||||
|
$SVN_URL ||= file_to_s("$GIT_DIR/$GIT_SVN/info/url");
|
||||||
|
chdir $SVN_WC or croak $!;
|
||||||
|
my %ign;
|
||||||
|
File::Find::find({wanted=>sub{if(lstat $_ && -d _ && -d "$_/.svn"){
|
||||||
|
s#^\./##;
|
||||||
|
@{$ign{$_}} = safe_qx(qw(svn propget svn:ignore),$_);
|
||||||
|
}}, no_chdir=>1},'.');
|
||||||
|
|
||||||
|
print "\n# /\n";
|
||||||
|
foreach (@{$ign{'.'}}) { print '/',$_ if /\S/ }
|
||||||
|
delete $ign{'.'};
|
||||||
|
foreach my $i (sort keys %ign) {
|
||||||
|
print "\n# ",$i,"\n";
|
||||||
|
foreach (@{$ign{$i}}) { print '/',$i,'/',$_ if /\S/ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
########################### utility functions #########################
|
########################### utility functions #########################
|
||||||
|
|
||||||
sub setup_git_svn {
|
sub setup_git_svn {
|
||||||
|
@ -566,6 +592,7 @@ sub handle_rmdir {
|
||||||
sub svn_commit_tree {
|
sub svn_commit_tree {
|
||||||
my ($svn_rev, $commit) = @_;
|
my ($svn_rev, $commit) = @_;
|
||||||
my $commit_msg = "$GIT_DIR/$GIT_SVN/.svn-commit.tmp.$$";
|
my $commit_msg = "$GIT_DIR/$GIT_SVN/.svn-commit.tmp.$$";
|
||||||
|
my %log_msg = ( msg => '' );
|
||||||
open my $msg, '>', $commit_msg or croak $!;
|
open my $msg, '>', $commit_msg or croak $!;
|
||||||
|
|
||||||
chomp(my $type = `git-cat-file -t $commit`);
|
chomp(my $type = `git-cat-file -t $commit`);
|
||||||
|
@ -581,6 +608,7 @@ sub svn_commit_tree {
|
||||||
if (!$in_msg) {
|
if (!$in_msg) {
|
||||||
$in_msg = 1 if (/^\s*$/);
|
$in_msg = 1 if (/^\s*$/);
|
||||||
} else {
|
} else {
|
||||||
|
$log_msg{msg} .= $_;
|
||||||
print $msg $_ or croak $!;
|
print $msg $_ or croak $!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -600,9 +628,30 @@ sub svn_commit_tree {
|
||||||
join("\n",@ci_output),"\n";
|
join("\n",@ci_output),"\n";
|
||||||
my ($rev_committed) = ($committed =~ /^Committed revision (\d+)\./);
|
my ($rev_committed) = ($committed =~ /^Committed revision (\d+)\./);
|
||||||
|
|
||||||
# resync immediately
|
my @svn_up = qw(svn up);
|
||||||
my @svn_up = (qw(svn up), "-r$svn_rev");
|
|
||||||
push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
|
push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
|
||||||
|
if ($rev_committed == ($svn_rev + 1)) {
|
||||||
|
push @svn_up, "-r$rev_committed";
|
||||||
|
sys(@svn_up);
|
||||||
|
my $info = svn_info('.');
|
||||||
|
my $date = $info->{'Last Changed Date'} or die "Missing date\n";
|
||||||
|
if ($info->{'Last Changed Rev'} != $rev_committed) {
|
||||||
|
croak "$info->{'Last Changed Rev'} != $rev_committed\n"
|
||||||
|
}
|
||||||
|
my ($Y,$m,$d,$H,$M,$S,$tz) = ($date =~
|
||||||
|
/(\d{4})\-(\d\d)\-(\d\d)\s
|
||||||
|
(\d\d)\:(\d\d)\:(\d\d)\s([\-\+]\d+)/x)
|
||||||
|
or croak "Failed to parse date: $date\n";
|
||||||
|
$log_msg{date} = "$tz $Y-$m-$d $H:$M:$S";
|
||||||
|
$log_msg{author} = $info->{'Last Changed Author'};
|
||||||
|
$log_msg{revision} = $rev_committed;
|
||||||
|
$log_msg{msg} .= "\n";
|
||||||
|
my $parent = file_to_s("$REV_DIR/$svn_rev");
|
||||||
|
git_commit(\%log_msg, $parent, $commit);
|
||||||
|
return $rev_committed;
|
||||||
|
}
|
||||||
|
# resync immediately
|
||||||
|
push @svn_up, "-r$svn_rev";
|
||||||
sys(@svn_up);
|
sys(@svn_up);
|
||||||
return fetch("$rev_committed=$commit")->{revision};
|
return fetch("$rev_committed=$commit")->{revision};
|
||||||
}
|
}
|
||||||
|
@ -699,7 +748,7 @@ sub svn_info {
|
||||||
# only single-lines seem to exist in svn info output
|
# only single-lines seem to exist in svn info output
|
||||||
while (<$info_fh>) {
|
while (<$info_fh>) {
|
||||||
chomp $_;
|
chomp $_;
|
||||||
if (m#^([^:]+)\s*:\s*(\S*)$#) {
|
if (m#^([^:]+)\s*:\s*(\S.*)$#) {
|
||||||
$ret->{$1} = $2;
|
$ret->{$1} = $2;
|
||||||
push @{$ret->{-order}}, $1;
|
push @{$ret->{-order}}, $1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,11 @@ rebuild::
|
||||||
the directory/repository you're tracking has moved or changed
|
the directory/repository you're tracking has moved or changed
|
||||||
protocols.
|
protocols.
|
||||||
|
|
||||||
|
show-ignore::
|
||||||
|
Recursively finds and lists the svn:ignore property on
|
||||||
|
directories. The output is suitable for appending to
|
||||||
|
the $GIT_DIR/info/exclude file.
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
-------
|
-------
|
||||||
-r <ARG>::
|
-r <ARG>::
|
||||||
|
@ -152,6 +157,8 @@ Tracking and contributing to an Subversion managed-project:
|
||||||
git commit git-svn-HEAD..my-branch
|
git commit git-svn-HEAD..my-branch
|
||||||
# Something is committed to SVN, pull the latest into your branch::
|
# Something is committed to SVN, pull the latest into your branch::
|
||||||
git-svn fetch && git pull . git-svn-HEAD
|
git-svn fetch && git pull . git-svn-HEAD
|
||||||
|
# Append svn:ignore settings to the default git exclude file:
|
||||||
|
git-svn show-ignore >> .git/info/exclude
|
||||||
|
|
||||||
DESIGN PHILOSOPHY
|
DESIGN PHILOSOPHY
|
||||||
-----------------
|
-----------------
|
||||||
|
|
|
@ -56,20 +56,6 @@ def show_date(epoch, tz):
|
||||||
|
|
||||||
return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(secs))
|
return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(secs))
|
||||||
|
|
||||||
def get_sha1_from_tags(line):
|
|
||||||
fp = os.popen("git cat-file -t " + line)
|
|
||||||
entry = string.strip(fp.readline())
|
|
||||||
fp.close()
|
|
||||||
if (entry == "commit"):
|
|
||||||
return line
|
|
||||||
elif (entry == "tag"):
|
|
||||||
fp = os.popen("git cat-file tag "+ line)
|
|
||||||
entry = string.strip(fp.readline())
|
|
||||||
fp.close()
|
|
||||||
obj = re.split(" ", entry)
|
|
||||||
if (obj[0] == "object"):
|
|
||||||
return obj[1]
|
|
||||||
return None
|
|
||||||
|
|
||||||
class CellRendererGraph(gtk.GenericCellRenderer):
|
class CellRendererGraph(gtk.GenericCellRenderer):
|
||||||
"""Cell renderer for directed graph.
|
"""Cell renderer for directed graph.
|
||||||
|
@ -168,15 +154,15 @@ class CellRendererGraph(gtk.GenericCellRenderer):
|
||||||
|
|
||||||
cols = self.node[0]
|
cols = self.node[0]
|
||||||
for start, end, colour in self.in_lines + self.out_lines:
|
for start, end, colour in self.in_lines + self.out_lines:
|
||||||
cols = max(cols, start, end)
|
cols = int(max(cols, start, end))
|
||||||
|
|
||||||
(column, colour, names) = self.node
|
(column, colour, names) = self.node
|
||||||
names_len = 0
|
names_len = 0
|
||||||
if (len(names) != 0):
|
if (len(names) != 0):
|
||||||
for item in names:
|
for item in names:
|
||||||
names_len += len(item)/3
|
names_len += len(item)
|
||||||
|
|
||||||
width = box_size * (cols + 1 + names_len )
|
width = box_size * (cols + 1 ) + names_len
|
||||||
height = box_size
|
height = box_size
|
||||||
|
|
||||||
# FIXME I have no idea how to use cell_area properly
|
# FIXME I have no idea how to use cell_area properly
|
||||||
|
@ -258,6 +244,8 @@ class CellRendererGraph(gtk.GenericCellRenderer):
|
||||||
for item in names:
|
for item in names:
|
||||||
name = name + item + " "
|
name = name + item + " "
|
||||||
|
|
||||||
|
ctx.select_font_face("Monospace")
|
||||||
|
ctx.set_font_size(13)
|
||||||
ctx.text_path(name)
|
ctx.text_path(name)
|
||||||
|
|
||||||
self.set_colour(ctx, colour, 0.0, 0.5)
|
self.set_colour(ctx, colour, 0.0, 0.5)
|
||||||
|
@ -434,7 +422,7 @@ class DiffWindow:
|
||||||
class GitView:
|
class GitView:
|
||||||
""" This is the main class
|
""" This is the main class
|
||||||
"""
|
"""
|
||||||
version = "0.6"
|
version = "0.7"
|
||||||
|
|
||||||
def __init__(self, with_diff=0):
|
def __init__(self, with_diff=0):
|
||||||
self.with_diff = with_diff
|
self.with_diff = with_diff
|
||||||
|
@ -465,32 +453,20 @@ class GitView:
|
||||||
respective sha1 details """
|
respective sha1 details """
|
||||||
|
|
||||||
self.bt_sha1 = { }
|
self.bt_sha1 = { }
|
||||||
git_dir = os.getenv("GIT_DIR")
|
ls_remote = re.compile('^(.{40})\trefs/([^^]+)(?:\\^(..))?$');
|
||||||
if (git_dir == None):
|
fp = os.popen('git ls-remote "${GIT_DIR-.git}"')
|
||||||
git_dir = ".git"
|
while 1:
|
||||||
|
line = string.strip(fp.readline())
|
||||||
#FIXME the path seperator
|
if line == '':
|
||||||
ref_files = os.listdir(git_dir + "/refs/tags")
|
break
|
||||||
for file in ref_files:
|
m = ls_remote.match(line)
|
||||||
fp = open(git_dir + "/refs/tags/"+file)
|
if not m:
|
||||||
sha1 = get_sha1_from_tags(string.strip(fp.readline()))
|
continue
|
||||||
try:
|
(sha1, name) = (m.group(1), m.group(2))
|
||||||
self.bt_sha1[sha1].append(file)
|
if not self.bt_sha1.has_key(sha1):
|
||||||
except KeyError:
|
self.bt_sha1[sha1] = []
|
||||||
self.bt_sha1[sha1] = [file]
|
self.bt_sha1[sha1].append(name)
|
||||||
fp.close()
|
fp.close()
|
||||||
|
|
||||||
|
|
||||||
#FIXME the path seperator
|
|
||||||
ref_files = os.listdir(git_dir + "/refs/heads")
|
|
||||||
for file in ref_files:
|
|
||||||
fp = open(git_dir + "/refs/heads/" + file)
|
|
||||||
sha1 = get_sha1_from_tags(string.strip(fp.readline()))
|
|
||||||
try:
|
|
||||||
self.bt_sha1[sha1].append(file)
|
|
||||||
except KeyError:
|
|
||||||
self.bt_sha1[sha1] = [file]
|
|
||||||
fp.close()
|
|
||||||
|
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
|
@ -537,8 +513,8 @@ class GitView:
|
||||||
|
|
||||||
cell = CellRendererGraph()
|
cell = CellRendererGraph()
|
||||||
column = gtk.TreeViewColumn()
|
column = gtk.TreeViewColumn()
|
||||||
column.set_resizable(False)
|
column.set_resizable(True)
|
||||||
column.pack_start(cell, expand=False)
|
column.pack_start(cell, expand=True)
|
||||||
column.add_attribute(cell, "node", 1)
|
column.add_attribute(cell, "node", 1)
|
||||||
column.add_attribute(cell, "in-lines", 2)
|
column.add_attribute(cell, "in-lines", 2)
|
||||||
column.add_attribute(cell, "out-lines", 3)
|
column.add_attribute(cell, "out-lines", 3)
|
||||||
|
@ -847,6 +823,7 @@ class GitView:
|
||||||
self.colours = {}
|
self.colours = {}
|
||||||
self.nodepos = {}
|
self.nodepos = {}
|
||||||
self.incomplete_line = {}
|
self.incomplete_line = {}
|
||||||
|
self.commits = []
|
||||||
|
|
||||||
index = 0
|
index = 0
|
||||||
last_colour = 0
|
last_colour = 0
|
||||||
|
@ -864,12 +841,7 @@ class GitView:
|
||||||
|
|
||||||
commit = Commit(commit_lines)
|
commit = Commit(commit_lines)
|
||||||
if (commit != None ):
|
if (commit != None ):
|
||||||
(out_line, last_colour, last_nodepos) = self.draw_graph(commit,
|
self.commits.append(commit)
|
||||||
index, out_line,
|
|
||||||
last_colour,
|
|
||||||
last_nodepos)
|
|
||||||
self.index[commit.commit_sha1] = index
|
|
||||||
index += 1
|
|
||||||
|
|
||||||
# Skip the '\0
|
# Skip the '\0
|
||||||
commit_lines = []
|
commit_lines = []
|
||||||
|
@ -878,6 +850,14 @@ class GitView:
|
||||||
|
|
||||||
fp.close()
|
fp.close()
|
||||||
|
|
||||||
|
for commit in self.commits:
|
||||||
|
(out_line, last_colour, last_nodepos) = self.draw_graph(commit,
|
||||||
|
index, out_line,
|
||||||
|
last_colour,
|
||||||
|
last_nodepos)
|
||||||
|
self.index[commit.commit_sha1] = index
|
||||||
|
index += 1
|
||||||
|
|
||||||
self.treeview.set_model(self.model)
|
self.treeview.set_model(self.model)
|
||||||
self.treeview.show()
|
self.treeview.show()
|
||||||
|
|
||||||
|
@ -890,28 +870,22 @@ class GitView:
|
||||||
|
|
||||||
# Reset nodepostion
|
# Reset nodepostion
|
||||||
if (last_nodepos > 5):
|
if (last_nodepos > 5):
|
||||||
last_nodepos = 0
|
last_nodepos = -1
|
||||||
|
|
||||||
# Add the incomplete lines of the last cell in this
|
# Add the incomplete lines of the last cell in this
|
||||||
for sha1 in self.incomplete_line.keys():
|
|
||||||
if ( sha1 != commit.commit_sha1):
|
|
||||||
for pos in self.incomplete_line[sha1]:
|
|
||||||
in_line.append((pos, pos, self.colours[sha1]))
|
|
||||||
else:
|
|
||||||
del self.incomplete_line[sha1]
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
colour = self.colours[commit.commit_sha1]
|
colour = self.colours[commit.commit_sha1]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
last_colour +=1
|
self.colours[commit.commit_sha1] = last_colour+1
|
||||||
self.colours[commit.commit_sha1] = last_colour
|
last_colour = self.colours[commit.commit_sha1]
|
||||||
colour = last_colour
|
colour = self.colours[commit.commit_sha1]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
node_pos = self.nodepos[commit.commit_sha1]
|
node_pos = self.nodepos[commit.commit_sha1]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
last_nodepos +=1
|
self.nodepos[commit.commit_sha1] = last_nodepos+1
|
||||||
self.nodepos[commit.commit_sha1] = last_nodepos
|
last_nodepos = self.nodepos[commit.commit_sha1]
|
||||||
node_pos = last_nodepos
|
node_pos = self.nodepos[commit.commit_sha1]
|
||||||
|
|
||||||
#The first parent always continue on the same line
|
#The first parent always continue on the same line
|
||||||
try:
|
try:
|
||||||
|
@ -921,25 +895,26 @@ class GitView:
|
||||||
self.colours[commit.parent_sha1[0]] = colour
|
self.colours[commit.parent_sha1[0]] = colour
|
||||||
self.nodepos[commit.parent_sha1[0]] = node_pos
|
self.nodepos[commit.parent_sha1[0]] = node_pos
|
||||||
|
|
||||||
in_line.append((node_pos, self.nodepos[commit.parent_sha1[0]],
|
for sha1 in self.incomplete_line.keys():
|
||||||
self.colours[commit.parent_sha1[0]]))
|
if (sha1 != commit.commit_sha1):
|
||||||
|
self.draw_incomplete_line(sha1, node_pos,
|
||||||
|
out_line, in_line, index)
|
||||||
|
else:
|
||||||
|
del self.incomplete_line[sha1]
|
||||||
|
|
||||||
self.add_incomplete_line(commit.parent_sha1[0], index+1)
|
|
||||||
|
|
||||||
if (len(commit.parent_sha1) > 1):
|
for parent_id in commit.parent_sha1:
|
||||||
for parent_id in commit.parent_sha1[1:]:
|
try:
|
||||||
try:
|
tmp_node_pos = self.nodepos[parent_id]
|
||||||
tmp_node_pos = self.nodepos[parent_id]
|
except KeyError:
|
||||||
except KeyError:
|
self.colours[parent_id] = last_colour+1
|
||||||
last_colour += 1;
|
last_colour = self.colours[parent_id]
|
||||||
self.colours[parent_id] = last_colour
|
self.nodepos[parent_id] = last_nodepos+1
|
||||||
last_nodepos +=1
|
last_nodepos = self.nodepos[parent_id]
|
||||||
self.nodepos[parent_id] = last_nodepos
|
|
||||||
|
|
||||||
in_line.append((node_pos, self.nodepos[parent_id],
|
|
||||||
self.colours[parent_id]))
|
|
||||||
self.add_incomplete_line(parent_id, index+1)
|
|
||||||
|
|
||||||
|
in_line.append((node_pos, self.nodepos[parent_id],
|
||||||
|
self.colours[parent_id]))
|
||||||
|
self.add_incomplete_line(parent_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
branch_tag = self.bt_sha1[commit.commit_sha1]
|
branch_tag = self.bt_sha1[commit.commit_sha1]
|
||||||
|
@ -954,12 +929,31 @@ class GitView:
|
||||||
|
|
||||||
return (in_line, last_colour, last_nodepos)
|
return (in_line, last_colour, last_nodepos)
|
||||||
|
|
||||||
def add_incomplete_line(self, sha1, index):
|
def add_incomplete_line(self, sha1):
|
||||||
try:
|
try:
|
||||||
self.incomplete_line[sha1].append(self.nodepos[sha1])
|
self.incomplete_line[sha1].append(self.nodepos[sha1])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.incomplete_line[sha1] = [self.nodepos[sha1]]
|
self.incomplete_line[sha1] = [self.nodepos[sha1]]
|
||||||
|
|
||||||
|
def draw_incomplete_line(self, sha1, node_pos, out_line, in_line, index):
|
||||||
|
for idx, pos in enumerate(self.incomplete_line[sha1]):
|
||||||
|
if(pos == node_pos):
|
||||||
|
#remove the straight line and add a slash
|
||||||
|
if ((pos, pos, self.colours[sha1]) in out_line):
|
||||||
|
out_line.remove((pos, pos, self.colours[sha1]))
|
||||||
|
out_line.append((pos, pos+0.5, self.colours[sha1]))
|
||||||
|
self.incomplete_line[sha1][idx] = pos = pos+0.5
|
||||||
|
try:
|
||||||
|
next_commit = self.commits[index+1]
|
||||||
|
if (next_commit.commit_sha1 == sha1 and pos != int(pos)):
|
||||||
|
# join the line back to the node point
|
||||||
|
# This need to be done only if we modified it
|
||||||
|
in_line.append((pos, pos-0.5, self.colours[sha1]))
|
||||||
|
continue;
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
in_line.append((pos, pos, self.colours[sha1]))
|
||||||
|
|
||||||
|
|
||||||
def _go_clicked_cb(self, widget, revid):
|
def _go_clicked_cb(self, widget, revid):
|
||||||
"""Callback for when the go button for a parent is clicked."""
|
"""Callback for when the go button for a parent is clicked."""
|
||||||
|
|
|
@ -193,7 +193,7 @@ int main(int argc, const char **argv)
|
||||||
show_file('-', ce);
|
show_file('-', ce);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
changed = ce_match_stat(ce, &st);
|
changed = ce_match_stat(ce, &st, 0);
|
||||||
if (!changed && !diff_options.find_copies_harder)
|
if (!changed && !diff_options.find_copies_harder)
|
||||||
continue;
|
continue;
|
||||||
oldmode = ntohl(ce->ce_mode);
|
oldmode = ntohl(ce->ce_mode);
|
||||||
|
|
|
@ -33,7 +33,7 @@ static int get_stat_data(struct cache_entry *ce,
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
changed = ce_match_stat(ce, &st);
|
changed = ce_match_stat(ce, &st, 0);
|
||||||
if (changed) {
|
if (changed) {
|
||||||
mode = create_ce_mode(st.st_mode);
|
mode = create_ce_mode(st.st_mode);
|
||||||
if (!trust_executable_bit &&
|
if (!trust_executable_bit &&
|
||||||
|
|
2
diff.c
2
diff.c
|
@ -311,7 +311,7 @@ static int work_tree_matches(const char *name, const unsigned char *sha1)
|
||||||
ce = active_cache[pos];
|
ce = active_cache[pos];
|
||||||
if ((lstat(name, &st) < 0) ||
|
if ((lstat(name, &st) < 0) ||
|
||||||
!S_ISREG(st.st_mode) || /* careful! */
|
!S_ISREG(st.st_mode) || /* careful! */
|
||||||
ce_match_stat(ce, &st) ||
|
ce_match_stat(ce, &st, 0) ||
|
||||||
memcmp(sha1, ce->sha1, 20))
|
memcmp(sha1, ce->sha1, 20))
|
||||||
return 0;
|
return 0;
|
||||||
/* we return 1 only when we can stat, it is a regular file,
|
/* we return 1 only when we can stat, it is a regular file,
|
||||||
|
|
|
@ -176,8 +176,10 @@ static int estimate_similarity(struct diff_filespec *src,
|
||||||
/* A delta that has a lot of literal additions would have
|
/* A delta that has a lot of literal additions would have
|
||||||
* big delta_size no matter what else it does.
|
* big delta_size no matter what else it does.
|
||||||
*/
|
*/
|
||||||
if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
|
if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE) {
|
||||||
|
free(delta);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Estimate the edit size by interpreting delta. */
|
/* Estimate the edit size by interpreting delta. */
|
||||||
if (count_delta(delta, delta_size, &src_copied, &literal_added)) {
|
if (count_delta(delta, delta_size, &src_copied, &literal_added)) {
|
||||||
|
|
2
entry.c
2
entry.c
|
@ -123,7 +123,7 @@ int checkout_entry(struct cache_entry *ce, struct checkout *state)
|
||||||
strcpy(path + len, ce->name);
|
strcpy(path + len, ce->name);
|
||||||
|
|
||||||
if (!lstat(path, &st)) {
|
if (!lstat(path, &st)) {
|
||||||
unsigned changed = ce_match_stat(ce, &st);
|
unsigned changed = ce_match_stat(ce, &st, 1);
|
||||||
if (!changed)
|
if (!changed)
|
||||||
return 0;
|
return 0;
|
||||||
if (!state->force) {
|
if (!state->force) {
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
char git_default_email[MAX_GITNAME];
|
char git_default_email[MAX_GITNAME];
|
||||||
char git_default_name[MAX_GITNAME];
|
char git_default_name[MAX_GITNAME];
|
||||||
int trust_executable_bit = 1;
|
int trust_executable_bit = 1;
|
||||||
|
int assume_unchanged = 0;
|
||||||
int only_use_symrefs = 0;
|
int only_use_symrefs = 0;
|
||||||
int repository_format_version = 0;
|
int repository_format_version = 0;
|
||||||
char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
|
char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
|
||||||
|
|
639
epoch.c
639
epoch.c
|
@ -1,639 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2005, Jon Seymour
|
|
||||||
*
|
|
||||||
* For more information about epoch theory on which this module is based,
|
|
||||||
* refer to http://blackcubes.dyndns.org/epoch/. That web page defines
|
|
||||||
* terms such as "epoch" and "minimal, non-linear epoch" and provides rationales
|
|
||||||
* for some of the algorithms used here.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
/* Provides arbitrary precision integers required to accurately represent
|
|
||||||
* fractional mass: */
|
|
||||||
#include <openssl/bn.h>
|
|
||||||
|
|
||||||
#include "cache.h"
|
|
||||||
#include "commit.h"
|
|
||||||
#include "epoch.h"
|
|
||||||
|
|
||||||
struct fraction {
|
|
||||||
BIGNUM numerator;
|
|
||||||
BIGNUM denominator;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define HAS_EXACTLY_ONE_PARENT(n) ((n)->parents && !(n)->parents->next)
|
|
||||||
|
|
||||||
static BN_CTX *context = NULL;
|
|
||||||
static struct fraction *one = NULL;
|
|
||||||
static struct fraction *zero = NULL;
|
|
||||||
|
|
||||||
static BN_CTX *get_BN_CTX(void)
|
|
||||||
{
|
|
||||||
if (!context) {
|
|
||||||
context = BN_CTX_new();
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct fraction *new_zero(void)
|
|
||||||
{
|
|
||||||
struct fraction *result = xmalloc(sizeof(*result));
|
|
||||||
BN_init(&result->numerator);
|
|
||||||
BN_init(&result->denominator);
|
|
||||||
BN_zero(&result->numerator);
|
|
||||||
BN_one(&result->denominator);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void clear_fraction(struct fraction *fraction)
|
|
||||||
{
|
|
||||||
BN_clear(&fraction->numerator);
|
|
||||||
BN_clear(&fraction->denominator);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct fraction *divide(struct fraction *result, struct fraction *fraction, int divisor)
|
|
||||||
{
|
|
||||||
BIGNUM bn_divisor;
|
|
||||||
|
|
||||||
BN_init(&bn_divisor);
|
|
||||||
BN_set_word(&bn_divisor, divisor);
|
|
||||||
|
|
||||||
BN_copy(&result->numerator, &fraction->numerator);
|
|
||||||
BN_mul(&result->denominator, &fraction->denominator, &bn_divisor, get_BN_CTX());
|
|
||||||
|
|
||||||
BN_clear(&bn_divisor);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct fraction *init_fraction(struct fraction *fraction)
|
|
||||||
{
|
|
||||||
BN_init(&fraction->numerator);
|
|
||||||
BN_init(&fraction->denominator);
|
|
||||||
BN_zero(&fraction->numerator);
|
|
||||||
BN_one(&fraction->denominator);
|
|
||||||
return fraction;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct fraction *get_one(void)
|
|
||||||
{
|
|
||||||
if (!one) {
|
|
||||||
one = new_zero();
|
|
||||||
BN_one(&one->numerator);
|
|
||||||
}
|
|
||||||
return one;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct fraction *get_zero(void)
|
|
||||||
{
|
|
||||||
if (!zero) {
|
|
||||||
zero = new_zero();
|
|
||||||
}
|
|
||||||
return zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct fraction *copy(struct fraction *to, struct fraction *from)
|
|
||||||
{
|
|
||||||
BN_copy(&to->numerator, &from->numerator);
|
|
||||||
BN_copy(&to->denominator, &from->denominator);
|
|
||||||
return to;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct fraction *add(struct fraction *result, struct fraction *left, struct fraction *right)
|
|
||||||
{
|
|
||||||
BIGNUM a, b, gcd;
|
|
||||||
|
|
||||||
BN_init(&a);
|
|
||||||
BN_init(&b);
|
|
||||||
BN_init(&gcd);
|
|
||||||
|
|
||||||
BN_mul(&a, &left->numerator, &right->denominator, get_BN_CTX());
|
|
||||||
BN_mul(&b, &left->denominator, &right->numerator, get_BN_CTX());
|
|
||||||
BN_mul(&result->denominator, &left->denominator, &right->denominator, get_BN_CTX());
|
|
||||||
BN_add(&result->numerator, &a, &b);
|
|
||||||
|
|
||||||
BN_gcd(&gcd, &result->denominator, &result->numerator, get_BN_CTX());
|
|
||||||
BN_div(&result->denominator, NULL, &result->denominator, &gcd, get_BN_CTX());
|
|
||||||
BN_div(&result->numerator, NULL, &result->numerator, &gcd, get_BN_CTX());
|
|
||||||
|
|
||||||
BN_clear(&a);
|
|
||||||
BN_clear(&b);
|
|
||||||
BN_clear(&gcd);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int compare(struct fraction *left, struct fraction *right)
|
|
||||||
{
|
|
||||||
BIGNUM a, b;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
BN_init(&a);
|
|
||||||
BN_init(&b);
|
|
||||||
|
|
||||||
BN_mul(&a, &left->numerator, &right->denominator, get_BN_CTX());
|
|
||||||
BN_mul(&b, &left->denominator, &right->numerator, get_BN_CTX());
|
|
||||||
|
|
||||||
result = BN_cmp(&a, &b);
|
|
||||||
|
|
||||||
BN_clear(&a);
|
|
||||||
BN_clear(&b);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct mass_counter {
|
|
||||||
struct fraction seen;
|
|
||||||
struct fraction pending;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct mass_counter *new_mass_counter(struct commit *commit, struct fraction *pending)
|
|
||||||
{
|
|
||||||
struct mass_counter *mass_counter = xmalloc(sizeof(*mass_counter));
|
|
||||||
memset(mass_counter, 0, sizeof(*mass_counter));
|
|
||||||
|
|
||||||
init_fraction(&mass_counter->seen);
|
|
||||||
init_fraction(&mass_counter->pending);
|
|
||||||
|
|
||||||
copy(&mass_counter->pending, pending);
|
|
||||||
copy(&mass_counter->seen, get_zero());
|
|
||||||
|
|
||||||
if (commit->object.util) {
|
|
||||||
die("multiple attempts to initialize mass counter for %s",
|
|
||||||
sha1_to_hex(commit->object.sha1));
|
|
||||||
}
|
|
||||||
|
|
||||||
commit->object.util = mass_counter;
|
|
||||||
|
|
||||||
return mass_counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_mass_counter(struct mass_counter *counter)
|
|
||||||
{
|
|
||||||
clear_fraction(&counter->seen);
|
|
||||||
clear_fraction(&counter->pending);
|
|
||||||
free(counter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Finds the base commit of a list of commits.
|
|
||||||
*
|
|
||||||
* One property of the commit being searched for is that every commit reachable
|
|
||||||
* from the base commit is reachable from the commits in the starting list only
|
|
||||||
* via paths that include the base commit.
|
|
||||||
*
|
|
||||||
* This algorithm uses a conservation of mass approach to find the base commit.
|
|
||||||
*
|
|
||||||
* We start by injecting one unit of mass into the graph at each
|
|
||||||
* of the commits in the starting list. Injecting mass into a commit
|
|
||||||
* is achieved by adding to its pending mass counter and, if it is not already
|
|
||||||
* enqueued, enqueuing the commit in a list of pending commits, in latest
|
|
||||||
* commit date first order.
|
|
||||||
*
|
|
||||||
* The algorithm then proceeds to visit each commit in the pending queue.
|
|
||||||
* Upon each visit, the pending mass is added to the mass already seen for that
|
|
||||||
* commit and then divided into N equal portions, where N is the number of
|
|
||||||
* parents of the commit being visited. The divided portions are then injected
|
|
||||||
* into each of the parents.
|
|
||||||
*
|
|
||||||
* The algorithm continues until we discover a commit which has seen all the
|
|
||||||
* mass originally injected or until we run out of things to do.
|
|
||||||
*
|
|
||||||
* If we find a commit that has seen all the original mass, we have found
|
|
||||||
* the common base of all the commits in the starting list.
|
|
||||||
*
|
|
||||||
* The algorithm does _not_ depend on accurate timestamps for correct operation.
|
|
||||||
* However, reasonably sane (e.g. non-random) timestamps are required in order
|
|
||||||
* to prevent an exponential performance characteristic. The occasional
|
|
||||||
* timestamp inaccuracy will not dramatically affect performance but may
|
|
||||||
* result in more nodes being processed than strictly necessary.
|
|
||||||
*
|
|
||||||
* This procedure sets *boundary to the address of the base commit. It returns
|
|
||||||
* non-zero if, and only if, there was a problem parsing one of the
|
|
||||||
* commits discovered during the traversal.
|
|
||||||
*/
|
|
||||||
static int find_base_for_list(struct commit_list *list, struct commit **boundary)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
struct commit_list *cleaner = NULL;
|
|
||||||
struct commit_list *pending = NULL;
|
|
||||||
struct fraction injected;
|
|
||||||
init_fraction(&injected);
|
|
||||||
*boundary = NULL;
|
|
||||||
|
|
||||||
for (; list; list = list->next) {
|
|
||||||
struct commit *item = list->item;
|
|
||||||
|
|
||||||
if (!item->object.util) {
|
|
||||||
new_mass_counter(list->item, get_one());
|
|
||||||
add(&injected, &injected, get_one());
|
|
||||||
|
|
||||||
commit_list_insert(list->item, &cleaner);
|
|
||||||
commit_list_insert(list->item, &pending);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!*boundary && pending && !ret) {
|
|
||||||
struct commit *latest = pop_commit(&pending);
|
|
||||||
struct mass_counter *latest_node = (struct mass_counter *) latest->object.util;
|
|
||||||
int num_parents;
|
|
||||||
|
|
||||||
if ((ret = parse_commit(latest)))
|
|
||||||
continue;
|
|
||||||
add(&latest_node->seen, &latest_node->seen, &latest_node->pending);
|
|
||||||
|
|
||||||
num_parents = count_parents(latest);
|
|
||||||
if (num_parents) {
|
|
||||||
struct fraction distribution;
|
|
||||||
struct commit_list *parents;
|
|
||||||
|
|
||||||
divide(init_fraction(&distribution), &latest_node->pending, num_parents);
|
|
||||||
|
|
||||||
for (parents = latest->parents; parents; parents = parents->next) {
|
|
||||||
struct commit *parent = parents->item;
|
|
||||||
struct mass_counter *parent_node = (struct mass_counter *) parent->object.util;
|
|
||||||
|
|
||||||
if (!parent_node) {
|
|
||||||
parent_node = new_mass_counter(parent, &distribution);
|
|
||||||
insert_by_date(parent, &pending);
|
|
||||||
commit_list_insert(parent, &cleaner);
|
|
||||||
} else {
|
|
||||||
if (!compare(&parent_node->pending, get_zero()))
|
|
||||||
insert_by_date(parent, &pending);
|
|
||||||
add(&parent_node->pending, &parent_node->pending, &distribution);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clear_fraction(&distribution);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!compare(&latest_node->seen, &injected))
|
|
||||||
*boundary = latest;
|
|
||||||
copy(&latest_node->pending, get_zero());
|
|
||||||
}
|
|
||||||
|
|
||||||
while (cleaner) {
|
|
||||||
struct commit *next = pop_commit(&cleaner);
|
|
||||||
free_mass_counter((struct mass_counter *) next->object.util);
|
|
||||||
next->object.util = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pending)
|
|
||||||
free_commit_list(pending);
|
|
||||||
|
|
||||||
clear_fraction(&injected);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Finds the base of an minimal, non-linear epoch, headed at head, by
|
|
||||||
* applying the find_base_for_list to a list consisting of the parents
|
|
||||||
*/
|
|
||||||
static int find_base(struct commit *head, struct commit **boundary)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
struct commit_list *pending = NULL;
|
|
||||||
struct commit_list *next;
|
|
||||||
|
|
||||||
for (next = head->parents; next; next = next->next) {
|
|
||||||
commit_list_insert(next->item, &pending);
|
|
||||||
}
|
|
||||||
ret = find_base_for_list(pending, boundary);
|
|
||||||
free_commit_list(pending);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This procedure traverses to the boundary of the first epoch in the epoch
|
|
||||||
* sequence of the epoch headed at head_of_epoch. This is either the end of
|
|
||||||
* the maximal linear epoch or the base of a minimal non-linear epoch.
|
|
||||||
*
|
|
||||||
* The queue of pending nodes is sorted in reverse date order and each node
|
|
||||||
* is currently in the queue at most once.
|
|
||||||
*/
|
|
||||||
static int find_next_epoch_boundary(struct commit *head_of_epoch, struct commit **boundary)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct commit *item = head_of_epoch;
|
|
||||||
|
|
||||||
ret = parse_commit(item);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (HAS_EXACTLY_ONE_PARENT(item)) {
|
|
||||||
/*
|
|
||||||
* We are at the start of a maximimal linear epoch.
|
|
||||||
* Traverse to the end.
|
|
||||||
*/
|
|
||||||
while (HAS_EXACTLY_ONE_PARENT(item) && !ret) {
|
|
||||||
item = item->parents->item;
|
|
||||||
ret = parse_commit(item);
|
|
||||||
}
|
|
||||||
*boundary = item;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* Otherwise, we are at the start of a minimal, non-linear
|
|
||||||
* epoch - find the common base of all parents.
|
|
||||||
*/
|
|
||||||
ret = find_base(item, boundary);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns non-zero if parent is known to be a parent of child.
|
|
||||||
*/
|
|
||||||
static int is_parent_of(struct commit *parent, struct commit *child)
|
|
||||||
{
|
|
||||||
struct commit_list *parents;
|
|
||||||
for (parents = child->parents; parents; parents = parents->next) {
|
|
||||||
if (!memcmp(parent->object.sha1, parents->item->object.sha1,
|
|
||||||
sizeof(parents->item->object.sha1)))
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Pushes an item onto the merge order stack. If the top of the stack is
|
|
||||||
* marked as being a possible "break", we check to see whether it actually
|
|
||||||
* is a break.
|
|
||||||
*/
|
|
||||||
static void push_onto_merge_order_stack(struct commit_list **stack, struct commit *item)
|
|
||||||
{
|
|
||||||
struct commit_list *top = *stack;
|
|
||||||
if (top && (top->item->object.flags & DISCONTINUITY)) {
|
|
||||||
if (is_parent_of(top->item, item)) {
|
|
||||||
top->item->object.flags &= ~DISCONTINUITY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
commit_list_insert(item, stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Marks all interesting, visited commits reachable from this commit
|
|
||||||
* as uninteresting. We stop recursing when we reach the epoch boundary,
|
|
||||||
* an unvisited node or a node that has already been marking uninteresting.
|
|
||||||
*
|
|
||||||
* This doesn't actually mark all ancestors between the start node and the
|
|
||||||
* epoch boundary uninteresting, but does ensure that they will eventually
|
|
||||||
* be marked uninteresting when the main sort_first_epoch() traversal
|
|
||||||
* eventually reaches them.
|
|
||||||
*/
|
|
||||||
static void mark_ancestors_uninteresting(struct commit *commit)
|
|
||||||
{
|
|
||||||
unsigned int flags = commit->object.flags;
|
|
||||||
int visited = flags & VISITED;
|
|
||||||
int boundary = flags & BOUNDARY;
|
|
||||||
int uninteresting = flags & UNINTERESTING;
|
|
||||||
struct commit_list *next;
|
|
||||||
|
|
||||||
commit->object.flags |= UNINTERESTING;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We only need to recurse if
|
|
||||||
* we are not on the boundary and
|
|
||||||
* we have not already been marked uninteresting and
|
|
||||||
* we have already been visited.
|
|
||||||
*
|
|
||||||
* The main sort_first_epoch traverse will mark unreachable
|
|
||||||
* all uninteresting, unvisited parents as they are visited
|
|
||||||
* so there is no need to duplicate that traversal here.
|
|
||||||
*
|
|
||||||
* Similarly, if we are already marked uninteresting
|
|
||||||
* then either all ancestors have already been marked
|
|
||||||
* uninteresting or will be once the sort_first_epoch
|
|
||||||
* traverse reaches them.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (uninteresting || boundary || !visited)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (next = commit->parents; next; next = next->next)
|
|
||||||
mark_ancestors_uninteresting(next->item);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sorts the nodes of the first epoch of the epoch sequence of the epoch headed at head
|
|
||||||
* into merge order.
|
|
||||||
*/
|
|
||||||
static void sort_first_epoch(struct commit *head, struct commit_list **stack)
|
|
||||||
{
|
|
||||||
struct commit_list *parents;
|
|
||||||
|
|
||||||
head->object.flags |= VISITED;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO: By sorting the parents in a different order, we can alter the
|
|
||||||
* merge order to show contemporaneous changes in parallel branches
|
|
||||||
* occurring after "local" changes. This is useful for a developer
|
|
||||||
* when a developer wants to see all changes that were incorporated
|
|
||||||
* into the same merge as her own changes occur after her own
|
|
||||||
* changes.
|
|
||||||
*/
|
|
||||||
|
|
||||||
for (parents = head->parents; parents; parents = parents->next) {
|
|
||||||
struct commit *parent = parents->item;
|
|
||||||
|
|
||||||
if (head->object.flags & UNINTERESTING) {
|
|
||||||
/*
|
|
||||||
* Propagates the uninteresting bit to all parents.
|
|
||||||
* if we have already visited this parent, then
|
|
||||||
* the uninteresting bit will be propagated to each
|
|
||||||
* reachable commit that is still not marked
|
|
||||||
* uninteresting and won't otherwise be reached.
|
|
||||||
*/
|
|
||||||
mark_ancestors_uninteresting(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(parent->object.flags & VISITED)) {
|
|
||||||
if (parent->object.flags & BOUNDARY) {
|
|
||||||
if (*stack) {
|
|
||||||
die("something else is on the stack - %s",
|
|
||||||
sha1_to_hex((*stack)->item->object.sha1));
|
|
||||||
}
|
|
||||||
push_onto_merge_order_stack(stack, parent);
|
|
||||||
parent->object.flags |= VISITED;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
sort_first_epoch(parent, stack);
|
|
||||||
if (parents) {
|
|
||||||
/*
|
|
||||||
* This indicates a possible
|
|
||||||
* discontinuity it may not be be
|
|
||||||
* actual discontinuity if the head
|
|
||||||
* of parent N happens to be the tail
|
|
||||||
* of parent N+1.
|
|
||||||
*
|
|
||||||
* The next push onto the stack will
|
|
||||||
* resolve the question.
|
|
||||||
*/
|
|
||||||
(*stack)->item->object.flags |= DISCONTINUITY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
push_onto_merge_order_stack(stack, head);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Emit the contents of the stack.
|
|
||||||
*
|
|
||||||
* The stack is freed and replaced by NULL.
|
|
||||||
*
|
|
||||||
* Sets the return value to STOP if no further output should be generated.
|
|
||||||
*/
|
|
||||||
static int emit_stack(struct commit_list **stack, emitter_func emitter, int include_last)
|
|
||||||
{
|
|
||||||
unsigned int seen = 0;
|
|
||||||
int action = CONTINUE;
|
|
||||||
|
|
||||||
while (*stack && (action != STOP)) {
|
|
||||||
struct commit *next = pop_commit(stack);
|
|
||||||
seen |= next->object.flags;
|
|
||||||
if (*stack || include_last) {
|
|
||||||
if (!*stack)
|
|
||||||
next->object.flags |= BOUNDARY;
|
|
||||||
action = emitter(next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*stack) {
|
|
||||||
free_commit_list(*stack);
|
|
||||||
*stack = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (action == STOP || (seen & UNINTERESTING)) ? STOP : CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sorts an arbitrary epoch into merge order by sorting each epoch
|
|
||||||
* of its epoch sequence into order.
|
|
||||||
*
|
|
||||||
* Note: this algorithm currently leaves traces of its execution in the
|
|
||||||
* object flags of nodes it discovers. This should probably be fixed.
|
|
||||||
*/
|
|
||||||
static int sort_in_merge_order(struct commit *head_of_epoch, emitter_func emitter)
|
|
||||||
{
|
|
||||||
struct commit *next = head_of_epoch;
|
|
||||||
int ret = 0;
|
|
||||||
int action = CONTINUE;
|
|
||||||
|
|
||||||
ret = parse_commit(head_of_epoch);
|
|
||||||
|
|
||||||
next->object.flags |= BOUNDARY;
|
|
||||||
|
|
||||||
while (next && next->parents && !ret && (action != STOP)) {
|
|
||||||
struct commit *base = NULL;
|
|
||||||
|
|
||||||
ret = find_next_epoch_boundary(next, &base);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
next->object.flags |= BOUNDARY;
|
|
||||||
if (base)
|
|
||||||
base->object.flags |= BOUNDARY;
|
|
||||||
|
|
||||||
if (HAS_EXACTLY_ONE_PARENT(next)) {
|
|
||||||
while (HAS_EXACTLY_ONE_PARENT(next)
|
|
||||||
&& (action != STOP)
|
|
||||||
&& !ret) {
|
|
||||||
if (next->object.flags & UNINTERESTING) {
|
|
||||||
action = STOP;
|
|
||||||
} else {
|
|
||||||
action = emitter(next);
|
|
||||||
}
|
|
||||||
if (action != STOP) {
|
|
||||||
next = next->parents->item;
|
|
||||||
ret = parse_commit(next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
struct commit_list *stack = NULL;
|
|
||||||
sort_first_epoch(next, &stack);
|
|
||||||
action = emit_stack(&stack, emitter, (base == NULL));
|
|
||||||
next = base;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next && (action != STOP) && !ret) {
|
|
||||||
emitter(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sorts the nodes reachable from a starting list in merge order, we
|
|
||||||
* first find the base for the starting list and then sort all nodes
|
|
||||||
* in this subgraph using the sort_first_epoch algorithm. Once we have
|
|
||||||
* reached the base we can continue sorting using sort_in_merge_order.
|
|
||||||
*/
|
|
||||||
int sort_list_in_merge_order(struct commit_list *list, emitter_func emitter)
|
|
||||||
{
|
|
||||||
struct commit_list *stack = NULL;
|
|
||||||
struct commit *base;
|
|
||||||
int ret = 0;
|
|
||||||
int action = CONTINUE;
|
|
||||||
struct commit_list *reversed = NULL;
|
|
||||||
|
|
||||||
for (; list; list = list->next)
|
|
||||||
commit_list_insert(list->item, &reversed);
|
|
||||||
|
|
||||||
if (!reversed)
|
|
||||||
return ret;
|
|
||||||
else if (!reversed->next) {
|
|
||||||
/*
|
|
||||||
* If there is only one element in the list, we can sort it
|
|
||||||
* using sort_in_merge_order.
|
|
||||||
*/
|
|
||||||
base = reversed->item;
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* Otherwise, we search for the base of the list.
|
|
||||||
*/
|
|
||||||
ret = find_base_for_list(reversed, &base);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
if (base)
|
|
||||||
base->object.flags |= BOUNDARY;
|
|
||||||
|
|
||||||
while (reversed) {
|
|
||||||
struct commit * next = pop_commit(&reversed);
|
|
||||||
|
|
||||||
if (!(next->object.flags & VISITED) && next!=base) {
|
|
||||||
sort_first_epoch(next, &stack);
|
|
||||||
if (reversed) {
|
|
||||||
/*
|
|
||||||
* If we have more commits
|
|
||||||
* to push, then the first
|
|
||||||
* push for the next parent may
|
|
||||||
* (or may * not) represent a
|
|
||||||
* discontinuity with respect
|
|
||||||
* to the parent currently on
|
|
||||||
* the top of the stack.
|
|
||||||
*
|
|
||||||
* Mark it for checking here,
|
|
||||||
* and check it with the next
|
|
||||||
* push. See sort_first_epoch()
|
|
||||||
* for more details.
|
|
||||||
*/
|
|
||||||
stack->item->object.flags |= DISCONTINUITY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
action = emit_stack(&stack, emitter, (base==NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (base && (action != STOP)) {
|
|
||||||
ret = sort_in_merge_order(base, emitter);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
21
epoch.h
21
epoch.h
|
@ -1,21 +0,0 @@
|
||||||
#ifndef EPOCH_H
|
|
||||||
#define EPOCH_H
|
|
||||||
|
|
||||||
|
|
||||||
// return codes for emitter_func
|
|
||||||
#define STOP 0
|
|
||||||
#define CONTINUE 1
|
|
||||||
#define DO 2
|
|
||||||
typedef int (*emitter_func) (struct commit *);
|
|
||||||
|
|
||||||
int sort_list_in_merge_order(struct commit_list *list, emitter_func emitter);
|
|
||||||
|
|
||||||
/* Low bits are used by rev-list */
|
|
||||||
#define UNINTERESTING (1u<<10)
|
|
||||||
#define BOUNDARY (1u<<11)
|
|
||||||
#define VISITED (1u<<12)
|
|
||||||
#define DISCONTINUITY (1u<<13)
|
|
||||||
#define LAST_EPOCH_FLAG (1u<<14)
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* EPOCH_H */
|
|
|
@ -13,7 +13,7 @@ void git_set_exec_path(const char *exec_path)
|
||||||
|
|
||||||
|
|
||||||
/* Returns the highest-priority, location to look for git programs. */
|
/* Returns the highest-priority, location to look for git programs. */
|
||||||
const char *git_exec_path()
|
const char *git_exec_path(void)
|
||||||
{
|
{
|
||||||
const char *env;
|
const char *env;
|
||||||
|
|
||||||
|
|
17
fetch-pack.c
17
fetch-pack.c
|
@ -8,7 +8,7 @@ static int keep_pack;
|
||||||
static int quiet;
|
static int quiet;
|
||||||
static int verbose;
|
static int verbose;
|
||||||
static const char fetch_pack_usage[] =
|
static const char fetch_pack_usage[] =
|
||||||
"git-fetch-pack [-q] [-v] [-k] [--exec=upload-pack] [host:]directory <refs>...";
|
"git-fetch-pack [-q] [-v] [-k] [--thin] [--exec=upload-pack] [host:]directory <refs>...";
|
||||||
static const char *exec = "git-upload-pack";
|
static const char *exec = "git-upload-pack";
|
||||||
|
|
||||||
#define COMPLETE (1U << 0)
|
#define COMPLETE (1U << 0)
|
||||||
|
@ -18,7 +18,7 @@ static const char *exec = "git-upload-pack";
|
||||||
#define POPPED (1U << 4)
|
#define POPPED (1U << 4)
|
||||||
|
|
||||||
static struct commit_list *rev_list = NULL;
|
static struct commit_list *rev_list = NULL;
|
||||||
static int non_common_revs = 0, multi_ack = 0;
|
static int non_common_revs = 0, multi_ack = 0, use_thin_pack = 0;
|
||||||
|
|
||||||
static void rev_list_push(struct commit *commit, int mark)
|
static void rev_list_push(struct commit *commit, int mark)
|
||||||
{
|
{
|
||||||
|
@ -82,7 +82,7 @@ static void mark_common(struct commit *commit,
|
||||||
Get the next rev to send, ignoring the common.
|
Get the next rev to send, ignoring the common.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const unsigned char* get_rev()
|
static const unsigned char* get_rev(void)
|
||||||
{
|
{
|
||||||
struct commit *commit = NULL;
|
struct commit *commit = NULL;
|
||||||
|
|
||||||
|
@ -156,8 +156,9 @@ static int find_common(int fd[2], unsigned char *result_sha1,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
packet_write(fd[1], "want %s%s\n", sha1_to_hex(remote),
|
packet_write(fd[1], "want %s%s%s\n", sha1_to_hex(remote),
|
||||||
multi_ack ? " multi_ack" : "");
|
(multi_ack ? " multi_ack" : ""),
|
||||||
|
(use_thin_pack ? " thin-pack" : ""));
|
||||||
fetching++;
|
fetching++;
|
||||||
}
|
}
|
||||||
packet_flush(fd[1]);
|
packet_flush(fd[1]);
|
||||||
|
@ -421,6 +422,10 @@ int main(int argc, char **argv)
|
||||||
keep_pack = 1;
|
keep_pack = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strcmp("--thin", arg)) {
|
||||||
|
use_thin_pack = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!strcmp("-v", arg)) {
|
if (!strcmp("-v", arg)) {
|
||||||
verbose = 1;
|
verbose = 1;
|
||||||
continue;
|
continue;
|
||||||
|
@ -434,6 +439,8 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
if (!dest)
|
if (!dest)
|
||||||
usage(fetch_pack_usage);
|
usage(fetch_pack_usage);
|
||||||
|
if (keep_pack)
|
||||||
|
use_thin_pack = 0;
|
||||||
pid = git_connect(fd, dest, exec);
|
pid = git_connect(fd, dest, exec);
|
||||||
if (pid < 0)
|
if (pid < 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -20,7 +20,7 @@ static int check_strict = 0;
|
||||||
static int keep_cache_objects = 0;
|
static int keep_cache_objects = 0;
|
||||||
static unsigned char head_sha1[20];
|
static unsigned char head_sha1[20];
|
||||||
|
|
||||||
#if NO_D_INO_IN_DIRENT
|
#ifdef NO_D_INO_IN_DIRENT
|
||||||
#define SORT_DIRENT 0
|
#define SORT_DIRENT 0
|
||||||
#define DIRENT_SORT_HINT(de) 0
|
#define DIRENT_SORT_HINT(de) 0
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -14,6 +14,10 @@ while : ; do
|
||||||
-v)
|
-v)
|
||||||
verbose=--verbose
|
verbose=--verbose
|
||||||
;;
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
-*)
|
-*)
|
||||||
usage
|
usage
|
||||||
;;
|
;;
|
||||||
|
|
10
git-am.sh
10
git-am.sh
|
@ -300,7 +300,7 @@ do
|
||||||
} >"$dotest/final-commit"
|
} >"$dotest/final-commit"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
case "$resolved,$interactive" in
|
case "$resolved$interactive" in
|
||||||
tt)
|
tt)
|
||||||
# This is used only for interactive view option.
|
# This is used only for interactive view option.
|
||||||
git-diff-index -p --cached HEAD >"$dotest/patch"
|
git-diff-index -p --cached HEAD >"$dotest/patch"
|
||||||
|
@ -364,6 +364,12 @@ do
|
||||||
# trust what the user has in the index file and the
|
# trust what the user has in the index file and the
|
||||||
# working tree.
|
# working tree.
|
||||||
resolved=
|
resolved=
|
||||||
|
changed="$(git-diff-index --cached --name-only HEAD)"
|
||||||
|
if test '' = "$changed"
|
||||||
|
then
|
||||||
|
echo "No changes - did you forget update-index?"
|
||||||
|
stop_here $this
|
||||||
|
fi
|
||||||
apply_status=0
|
apply_status=0
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
@ -374,7 +380,7 @@ do
|
||||||
then
|
then
|
||||||
# Applying the patch to an earlier tree and merging the
|
# Applying the patch to an earlier tree and merging the
|
||||||
# result may have produced the same tree as ours.
|
# result may have produced the same tree as ours.
|
||||||
changed="$(git-diff-index --cached --name-only -z HEAD)"
|
changed="$(git-diff-index --cached --name-only HEAD)"
|
||||||
if test '' = "$changed"
|
if test '' = "$changed"
|
||||||
then
|
then
|
||||||
echo No changes -- Patch already applied.
|
echo No changes -- Patch already applied.
|
||||||
|
|
|
@ -0,0 +1,488 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
# Copyright 2006, Ryan Anderson <ryan@michonline.com>
|
||||||
|
#
|
||||||
|
# GPL v2 (See COPYING)
|
||||||
|
#
|
||||||
|
# This file is licensed under the GPL v2, or a later version
|
||||||
|
# at the discretion of Linus Torvalds.
|
||||||
|
|
||||||
|
use warnings;
|
||||||
|
use strict;
|
||||||
|
use Getopt::Long;
|
||||||
|
use POSIX qw(strftime gmtime);
|
||||||
|
|
||||||
|
sub usage() {
|
||||||
|
print STDERR 'Usage: ${\basename $0} [-s] [-S revs-file] file [ revision ]
|
||||||
|
-l, --long
|
||||||
|
Show long rev (Defaults off)
|
||||||
|
-r, --rename
|
||||||
|
Follow renames (Defaults on).
|
||||||
|
-S, --rev-file revs-file
|
||||||
|
use revs from revs-file instead of calling git-rev-list
|
||||||
|
-h, --help
|
||||||
|
This message.
|
||||||
|
';
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
our ($help, $longrev, $rename, $starting_rev, $rev_file) = (0, 0, 1);
|
||||||
|
|
||||||
|
my $rc = GetOptions( "long|l" => \$longrev,
|
||||||
|
"help|h" => \$help,
|
||||||
|
"rename|r" => \$rename,
|
||||||
|
"rev-file|S" => \$rev_file);
|
||||||
|
if (!$rc or $help) {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
my $filename = shift @ARGV;
|
||||||
|
if (@ARGV) {
|
||||||
|
$starting_rev = shift @ARGV;
|
||||||
|
}
|
||||||
|
|
||||||
|
my @stack = (
|
||||||
|
{
|
||||||
|
'rev' => defined $starting_rev ? $starting_rev : "HEAD",
|
||||||
|
'filename' => $filename,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
our @filelines = ();
|
||||||
|
|
||||||
|
if (defined $starting_rev) {
|
||||||
|
@filelines = git_cat_file($starting_rev, $filename);
|
||||||
|
} else {
|
||||||
|
open(F,"<",$filename)
|
||||||
|
or die "Failed to open filename: $!";
|
||||||
|
|
||||||
|
while(<F>) {
|
||||||
|
chomp;
|
||||||
|
push @filelines, $_;
|
||||||
|
}
|
||||||
|
close(F);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
our %revs;
|
||||||
|
our @revqueue;
|
||||||
|
our $head;
|
||||||
|
|
||||||
|
my $revsprocessed = 0;
|
||||||
|
while (my $bound = pop @stack) {
|
||||||
|
my @revisions = git_rev_list($bound->{'rev'}, $bound->{'filename'});
|
||||||
|
foreach my $revinst (@revisions) {
|
||||||
|
my ($rev, @parents) = @$revinst;
|
||||||
|
$head ||= $rev;
|
||||||
|
|
||||||
|
if (!defined($rev)) {
|
||||||
|
$rev = "";
|
||||||
|
}
|
||||||
|
$revs{$rev}{'filename'} = $bound->{'filename'};
|
||||||
|
if (scalar @parents > 0) {
|
||||||
|
$revs{$rev}{'parents'} = \@parents;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$rename) {
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $newbound = find_parent_renames($rev, $bound->{'filename'});
|
||||||
|
if ( exists $newbound->{'filename'} && $newbound->{'filename'} ne $bound->{'filename'}) {
|
||||||
|
push @stack, $newbound;
|
||||||
|
$revs{$rev}{'parents'} = [$newbound->{'rev'}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
push @revqueue, $head;
|
||||||
|
init_claim( defined $starting_rev ? $starting_rev : 'dirty');
|
||||||
|
unless (defined $starting_rev) {
|
||||||
|
my $diff = open_pipe("git","diff","-R", "HEAD", "--",$filename)
|
||||||
|
or die "Failed to call git diff to check for dirty state: $!";
|
||||||
|
|
||||||
|
_git_diff_parse($diff, $head, "dirty", (
|
||||||
|
'author' => gitvar_name("GIT_AUTHOR_IDENT"),
|
||||||
|
'author_date' => sprintf("%s +0000",time()),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
close($diff);
|
||||||
|
}
|
||||||
|
handle_rev();
|
||||||
|
|
||||||
|
|
||||||
|
my $i = 0;
|
||||||
|
foreach my $l (@filelines) {
|
||||||
|
my ($output, $rev, $committer, $date);
|
||||||
|
if (ref $l eq 'ARRAY') {
|
||||||
|
($output, $rev, $committer, $date) = @$l;
|
||||||
|
if (!$longrev && length($rev) > 8) {
|
||||||
|
$rev = substr($rev,0,8);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$output = $l;
|
||||||
|
($rev, $committer, $date) = ('unknown', 'unknown', 'unknown');
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s\t(%10s\t%10s\t%d)%s\n", $rev, $committer,
|
||||||
|
format_date($date), $i++, $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub init_claim {
|
||||||
|
my ($rev) = @_;
|
||||||
|
for (my $i = 0; $i < @filelines; $i++) {
|
||||||
|
$filelines[$i] = [ $filelines[$i], '', '', '', 1];
|
||||||
|
# line,
|
||||||
|
# rev,
|
||||||
|
# author,
|
||||||
|
# date,
|
||||||
|
# 1 <-- belongs to the original file.
|
||||||
|
}
|
||||||
|
$revs{$rev}{'lines'} = \@filelines;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub handle_rev {
|
||||||
|
my $i = 0;
|
||||||
|
my %seen;
|
||||||
|
while (my $rev = shift @revqueue) {
|
||||||
|
next if $seen{$rev}++;
|
||||||
|
|
||||||
|
my %revinfo = git_commit_info($rev);
|
||||||
|
|
||||||
|
foreach my $p (@{$revs{$rev}{'parents'}}) {
|
||||||
|
|
||||||
|
git_diff_parse($p, $rev, %revinfo);
|
||||||
|
push @revqueue, $p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (scalar @{$revs{$rev}{parents}} == 0) {
|
||||||
|
# We must be at the initial rev here, so claim everything that is left.
|
||||||
|
for (my $i = 0; $i < @{$revs{$rev}{lines}}; $i++) {
|
||||||
|
if (ref ${$revs{$rev}{lines}}[$i] eq '' || ${$revs{$rev}{lines}}[$i][1] eq '') {
|
||||||
|
claim_line($i, $rev, $revs{$rev}{lines}, %revinfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub git_rev_list {
|
||||||
|
my ($rev, $file) = @_;
|
||||||
|
|
||||||
|
my $revlist;
|
||||||
|
if ($rev_file) {
|
||||||
|
open($revlist, '<' . $rev_file);
|
||||||
|
} else {
|
||||||
|
$revlist = open_pipe("git-rev-list","--parents","--remove-empty",$rev,"--",$file)
|
||||||
|
or die "Failed to exec git-rev-list: $!";
|
||||||
|
}
|
||||||
|
|
||||||
|
my @revs;
|
||||||
|
while(my $line = <$revlist>) {
|
||||||
|
chomp $line;
|
||||||
|
my ($rev, @parents) = split /\s+/, $line;
|
||||||
|
push @revs, [ $rev, @parents ];
|
||||||
|
}
|
||||||
|
close($revlist);
|
||||||
|
|
||||||
|
printf("0 revs found for rev %s (%s)\n", $rev, $file) if (@revs == 0);
|
||||||
|
return @revs;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub find_parent_renames {
|
||||||
|
my ($rev, $file) = @_;
|
||||||
|
|
||||||
|
my $patch = open_pipe("git-diff-tree", "-M50", "-r","--name-status", "-z","$rev")
|
||||||
|
or die "Failed to exec git-diff: $!";
|
||||||
|
|
||||||
|
local $/ = "\0";
|
||||||
|
my %bound;
|
||||||
|
my $junk = <$patch>;
|
||||||
|
while (my $change = <$patch>) {
|
||||||
|
chomp $change;
|
||||||
|
my $filename = <$patch>;
|
||||||
|
chomp $filename;
|
||||||
|
|
||||||
|
if ($change =~ m/^[AMD]$/ ) {
|
||||||
|
next;
|
||||||
|
} elsif ($change =~ m/^R/ ) {
|
||||||
|
my $oldfilename = $filename;
|
||||||
|
$filename = <$patch>;
|
||||||
|
chomp $filename;
|
||||||
|
if ( $file eq $filename ) {
|
||||||
|
my $parent = git_find_parent($rev, $oldfilename);
|
||||||
|
@bound{'rev','filename'} = ($parent, $oldfilename);
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close($patch);
|
||||||
|
|
||||||
|
return \%bound;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub git_find_parent {
|
||||||
|
my ($rev, $filename) = @_;
|
||||||
|
|
||||||
|
my $revparent = open_pipe("git-rev-list","--remove-empty", "--parents","--max-count=1","$rev","--",$filename)
|
||||||
|
or die "Failed to open git-rev-list to find a single parent: $!";
|
||||||
|
|
||||||
|
my $parentline = <$revparent>;
|
||||||
|
chomp $parentline;
|
||||||
|
my ($revfound,$parent) = split m/\s+/, $parentline;
|
||||||
|
|
||||||
|
close($revparent);
|
||||||
|
|
||||||
|
return $parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Get a diff between the current revision and a parent.
|
||||||
|
# Record the commit information that results.
|
||||||
|
sub git_diff_parse {
|
||||||
|
my ($parent, $rev, %revinfo) = @_;
|
||||||
|
|
||||||
|
my $diff = open_pipe("git-diff-tree","-M","-p",$rev,$parent,"--",
|
||||||
|
$revs{$rev}{'filename'}, $revs{$parent}{'filename'})
|
||||||
|
or die "Failed to call git-diff for annotation: $!";
|
||||||
|
|
||||||
|
_git_diff_parse($diff, $parent, $rev, %revinfo);
|
||||||
|
|
||||||
|
close($diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _git_diff_parse {
|
||||||
|
my ($diff, $parent, $rev, %revinfo) = @_;
|
||||||
|
|
||||||
|
my ($ri, $pi) = (0,0);
|
||||||
|
my $slines = $revs{$rev}{'lines'};
|
||||||
|
my @plines;
|
||||||
|
|
||||||
|
my $gotheader = 0;
|
||||||
|
my ($remstart);
|
||||||
|
my ($hunk_start, $hunk_index);
|
||||||
|
while(<$diff>) {
|
||||||
|
chomp;
|
||||||
|
if (m/^@@ -(\d+),(\d+) \+(\d+),(\d+)/) {
|
||||||
|
$remstart = $1;
|
||||||
|
# Adjust for 0-based arrays
|
||||||
|
$remstart--;
|
||||||
|
# Reinit hunk tracking.
|
||||||
|
$hunk_start = $remstart;
|
||||||
|
$hunk_index = 0;
|
||||||
|
$gotheader = 1;
|
||||||
|
|
||||||
|
for (my $i = $ri; $i < $remstart; $i++) {
|
||||||
|
$plines[$pi++] = $slines->[$i];
|
||||||
|
$ri++;
|
||||||
|
}
|
||||||
|
next;
|
||||||
|
} elsif (!$gotheader) {
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m/^\+(.*)$/) {
|
||||||
|
my $line = $1;
|
||||||
|
$plines[$pi++] = [ $line, '', '', '', 0 ];
|
||||||
|
next;
|
||||||
|
|
||||||
|
} elsif (m/^-(.*)$/) {
|
||||||
|
my $line = $1;
|
||||||
|
if (get_line($slines, $ri) eq $line) {
|
||||||
|
# Found a match, claim
|
||||||
|
claim_line($ri, $rev, $slines, %revinfo);
|
||||||
|
} else {
|
||||||
|
die sprintf("Sync error: %d/%d\n|%s\n|%s\n%s => %s\n",
|
||||||
|
$ri, $hunk_start + $hunk_index,
|
||||||
|
$line,
|
||||||
|
get_line($slines, $ri),
|
||||||
|
$rev, $parent);
|
||||||
|
}
|
||||||
|
$ri++;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (substr($_,1) ne get_line($slines,$ri) ) {
|
||||||
|
die sprintf("Line %d (%d) does not match:\n|%s\n|%s\n%s => %s\n",
|
||||||
|
$hunk_start + $hunk_index, $ri,
|
||||||
|
substr($_,1),
|
||||||
|
get_line($slines,$ri),
|
||||||
|
$rev, $parent);
|
||||||
|
}
|
||||||
|
$plines[$pi++] = $slines->[$ri++];
|
||||||
|
}
|
||||||
|
$hunk_index++;
|
||||||
|
}
|
||||||
|
for (my $i = $ri; $i < @{$slines} ; $i++) {
|
||||||
|
push @plines, $slines->[$ri++];
|
||||||
|
}
|
||||||
|
|
||||||
|
$revs{$parent}{lines} = \@plines;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_line {
|
||||||
|
my ($lines, $index) = @_;
|
||||||
|
|
||||||
|
return ref $lines->[$index] ne '' ? $lines->[$index][0] : $lines->[$index];
|
||||||
|
}
|
||||||
|
|
||||||
|
sub git_cat_file {
|
||||||
|
my ($rev, $filename) = @_;
|
||||||
|
return () unless defined $rev && defined $filename;
|
||||||
|
|
||||||
|
my $blob = git_ls_tree($rev, $filename);
|
||||||
|
|
||||||
|
my $catfile = open_pipe("git","cat-file", "blob", $blob)
|
||||||
|
or die "Failed to git-cat-file blob $blob (rev $rev, file $filename): " . $!;
|
||||||
|
|
||||||
|
my @lines;
|
||||||
|
while(<$catfile>) {
|
||||||
|
chomp;
|
||||||
|
push @lines, $_;
|
||||||
|
}
|
||||||
|
close($catfile);
|
||||||
|
|
||||||
|
return @lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub git_ls_tree {
|
||||||
|
my ($rev, $filename) = @_;
|
||||||
|
|
||||||
|
my $lstree = open_pipe("git","ls-tree",$rev,$filename)
|
||||||
|
or die "Failed to call git ls-tree: $!";
|
||||||
|
|
||||||
|
my ($mode, $type, $blob, $tfilename);
|
||||||
|
while(<$lstree>) {
|
||||||
|
($mode, $type, $blob, $tfilename) = split(/\s+/, $_, 4);
|
||||||
|
last if ($tfilename eq $filename);
|
||||||
|
}
|
||||||
|
close($lstree);
|
||||||
|
|
||||||
|
return $blob if $filename eq $filename;
|
||||||
|
die "git-ls-tree failed to find blob for $filename";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sub claim_line {
|
||||||
|
my ($floffset, $rev, $lines, %revinfo) = @_;
|
||||||
|
my $oline = get_line($lines, $floffset);
|
||||||
|
@{$lines->[$floffset]} = ( $oline, $rev,
|
||||||
|
$revinfo{'author'}, $revinfo{'author_date'} );
|
||||||
|
#printf("Claiming line %d with rev %s: '%s'\n",
|
||||||
|
# $floffset, $rev, $oline) if 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub git_commit_info {
|
||||||
|
my ($rev) = @_;
|
||||||
|
my $commit = open_pipe("git-cat-file", "commit", $rev)
|
||||||
|
or die "Failed to call git-cat-file: $!";
|
||||||
|
|
||||||
|
my %info;
|
||||||
|
while(<$commit>) {
|
||||||
|
chomp;
|
||||||
|
last if (length $_ == 0);
|
||||||
|
|
||||||
|
if (m/^author (.*) <(.*)> (.*)$/) {
|
||||||
|
$info{'author'} = $1;
|
||||||
|
$info{'author_email'} = $2;
|
||||||
|
$info{'author_date'} = $3;
|
||||||
|
} elsif (m/^committer (.*) <(.*)> (.*)$/) {
|
||||||
|
$info{'committer'} = $1;
|
||||||
|
$info{'committer_email'} = $2;
|
||||||
|
$info{'committer_date'} = $3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close($commit);
|
||||||
|
|
||||||
|
return %info;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub format_date {
|
||||||
|
my ($timestamp, $timezone) = split(' ', $_[0]);
|
||||||
|
|
||||||
|
return strftime("%Y-%m-%d %H:%M:%S " . $timezone, gmtime($timestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
# Copied from git-send-email.perl - We need a Git.pm module..
|
||||||
|
sub gitvar {
|
||||||
|
my ($var) = @_;
|
||||||
|
my $fh;
|
||||||
|
my $pid = open($fh, '-|');
|
||||||
|
die "$!" unless defined $pid;
|
||||||
|
if (!$pid) {
|
||||||
|
exec('git-var', $var) or die "$!";
|
||||||
|
}
|
||||||
|
my ($val) = <$fh>;
|
||||||
|
close $fh or die "$!";
|
||||||
|
chomp($val);
|
||||||
|
return $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub gitvar_name {
|
||||||
|
my ($name) = @_;
|
||||||
|
my $val = gitvar($name);
|
||||||
|
my @field = split(/\s+/, $val);
|
||||||
|
return join(' ', @field[0...(@field-4)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub open_pipe {
|
||||||
|
if ($^O eq '##INSERT_ACTIVESTATE_STRING_HERE##') {
|
||||||
|
return open_pipe_activestate(@_);
|
||||||
|
} else {
|
||||||
|
return open_pipe_normal(@_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub open_pipe_activestate {
|
||||||
|
tie *fh, "Git::ActiveStatePipe", @_;
|
||||||
|
return *fh;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub open_pipe_normal {
|
||||||
|
my (@execlist) = @_;
|
||||||
|
|
||||||
|
my $pid = open my $kid, "-|";
|
||||||
|
defined $pid or die "Cannot fork: $!";
|
||||||
|
|
||||||
|
unless ($pid) {
|
||||||
|
exec @execlist;
|
||||||
|
die "Cannot exec @execlist: $!";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $kid;
|
||||||
|
}
|
||||||
|
|
||||||
|
package Git::ActiveStatePipe;
|
||||||
|
use strict;
|
||||||
|
|
||||||
|
sub TIEHANDLE {
|
||||||
|
my ($class, @params) = @_;
|
||||||
|
my $cmdline = join " ", @params;
|
||||||
|
my @data = qx{$cmdline};
|
||||||
|
bless { i => 0, data => \@data }, $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub READLINE {
|
||||||
|
my $self = shift;
|
||||||
|
if ($self->{i} >= scalar @{$self->{data}}) {
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
return $self->{'data'}->[ $self->{i}++ ];
|
||||||
|
}
|
||||||
|
|
||||||
|
sub CLOSE {
|
||||||
|
my $self = shift;
|
||||||
|
delete $self->{data};
|
||||||
|
delete $self->{i};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub EOF {
|
||||||
|
my $self = shift;
|
||||||
|
return ($self->{i} >= scalar @{$self->{data}});
|
||||||
|
}
|
|
@ -928,7 +928,7 @@ sub find_parents {
|
||||||
|
|
||||||
# now walk up to the mergepoint collecting what patches we have
|
# now walk up to the mergepoint collecting what patches we have
|
||||||
my $branchtip = git_rev_parse($ps->{branch});
|
my $branchtip = git_rev_parse($ps->{branch});
|
||||||
my @ancestors = `git-rev-list --merge-order $branchtip ^$mergebase`;
|
my @ancestors = `git-rev-list --topo-order $branchtip ^$mergebase`;
|
||||||
my %have; # collected merges this branch has
|
my %have; # collected merges this branch has
|
||||||
foreach my $merge (@{$ps->{merges}}) {
|
foreach my $merge (@{$ps->{merges}}) {
|
||||||
$have{$merge} = 1;
|
$have{$merge} = 1;
|
||||||
|
@ -951,7 +951,7 @@ sub find_parents {
|
||||||
# see what the remote branch has - these are the merges we
|
# see what the remote branch has - these are the merges we
|
||||||
# will want to have in a consecutive series from the mergebase
|
# will want to have in a consecutive series from the mergebase
|
||||||
my $otherbranchtip = git_rev_parse($branch);
|
my $otherbranchtip = git_rev_parse($branch);
|
||||||
my @needraw = `git-rev-list --merge-order $otherbranchtip ^$mergebase`;
|
my @needraw = `git-rev-list --topo-order $otherbranchtip ^$mergebase`;
|
||||||
my @need;
|
my @need;
|
||||||
foreach my $needps (@needraw) { # get the psets
|
foreach my $needps (@needraw) { # get the psets
|
||||||
$needps = commitid2pset($needps);
|
$needps = commitid2pset($needps);
|
||||||
|
|
|
@ -118,7 +118,7 @@ dir="$2"
|
||||||
[ -e "$dir" ] && echo "$dir already exists." && usage
|
[ -e "$dir" ] && echo "$dir already exists." && usage
|
||||||
mkdir -p "$dir" &&
|
mkdir -p "$dir" &&
|
||||||
D=$(cd "$dir" && pwd) &&
|
D=$(cd "$dir" && pwd) &&
|
||||||
trap 'err=$?; rm -r $D; exit $err' exit
|
trap 'err=$?; cd ..; rm -r "$D"; exit $err' exit
|
||||||
case "$bare" in
|
case "$bare" in
|
||||||
yes) GIT_DIR="$D" ;;
|
yes) GIT_DIR="$D" ;;
|
||||||
*) GIT_DIR="$D/.git" ;;
|
*) GIT_DIR="$D/.git" ;;
|
||||||
|
@ -253,7 +253,7 @@ Pull: $head_points_at:$origin" &&
|
||||||
|
|
||||||
case "$no_checkout" in
|
case "$no_checkout" in
|
||||||
'')
|
'')
|
||||||
git checkout
|
git-read-tree -m -u -v HEAD HEAD
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
@ -846,8 +846,12 @@ while(<CVS>) {
|
||||||
print "Drop $fn\n" if $opt_v;
|
print "Drop $fn\n" if $opt_v;
|
||||||
} else {
|
} else {
|
||||||
print "".($init ? "New" : "Update")." $fn: $size bytes\n" if $opt_v;
|
print "".($init ? "New" : "Update")." $fn: $size bytes\n" if $opt_v;
|
||||||
open my $F, '-|', "git-hash-object -w $tmpname"
|
my $pid = open(my $F, '-|');
|
||||||
|
die $! unless defined $pid;
|
||||||
|
if (!$pid) {
|
||||||
|
exec("git-hash-object", "-w", $tmpname)
|
||||||
or die "Cannot create object: $!\n";
|
or die "Cannot create object: $!\n";
|
||||||
|
}
|
||||||
my $sha = <$F>;
|
my $sha = <$F>;
|
||||||
chomp $sha;
|
chomp $sha;
|
||||||
close $F;
|
close $F;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
36
git-fetch.sh
36
git-fetch.sh
|
@ -164,6 +164,7 @@ fast_forward_local () {
|
||||||
;;
|
;;
|
||||||
*,$local)
|
*,$local)
|
||||||
echo >&2 "* $1: fast forward to $3"
|
echo >&2 "* $1: fast forward to $3"
|
||||||
|
echo >&2 " from $local to $2"
|
||||||
git-update-ref "$1" "$2" "$local"
|
git-update-ref "$1" "$2" "$local"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
@ -320,7 +321,7 @@ fetch_main () {
|
||||||
( : subshell because we muck with IFS
|
( : subshell because we muck with IFS
|
||||||
IFS=" $LF"
|
IFS=" $LF"
|
||||||
(
|
(
|
||||||
git-fetch-pack $exec $keep "$remote" $rref || echo failed "$remote"
|
git-fetch-pack $exec $keep --thin "$remote" $rref || echo failed "$remote"
|
||||||
) |
|
) |
|
||||||
while read sha1 remote_name
|
while read sha1 remote_name
|
||||||
do
|
do
|
||||||
|
@ -368,20 +369,25 @@ fetch_main "$reflist"
|
||||||
# automated tag following
|
# automated tag following
|
||||||
case "$no_tags$tags" in
|
case "$no_tags$tags" in
|
||||||
'')
|
'')
|
||||||
taglist=$(IFS=" " &&
|
case "$reflist" in
|
||||||
git-ls-remote $upload_pack --tags "$remote" |
|
*:refs/*)
|
||||||
sed -ne 's|^\([0-9a-f]*\)[ ]\(refs/tags/.*\)^{}$|\1 \2|p' |
|
# effective only when we are following remote branch
|
||||||
while read sha1 name
|
# using local tracking branch.
|
||||||
do
|
taglist=$(IFS=" " &&
|
||||||
test -f "$GIT_DIR/$name" && continue
|
git-ls-remote $upload_pack --tags "$remote" |
|
||||||
git-check-ref-format "$name" || {
|
sed -ne 's|^\([0-9a-f]*\)[ ]\(refs/tags/.*\)^{}$|\1 \2|p' |
|
||||||
echo >&2 "warning: tag ${name} ignored"
|
while read sha1 name
|
||||||
continue
|
do
|
||||||
}
|
test -f "$GIT_DIR/$name" && continue
|
||||||
git-cat-file -t "$sha1" >/dev/null 2>&1 || continue
|
git-check-ref-format "$name" || {
|
||||||
echo >&2 "Auto-following $name"
|
echo >&2 "warning: tag ${name} ignored"
|
||||||
echo ".${name}:${name}"
|
continue
|
||||||
done)
|
}
|
||||||
|
git-cat-file -t "$sha1" >/dev/null 2>&1 || continue
|
||||||
|
echo >&2 "Auto-following $name"
|
||||||
|
echo ".${name}:${name}"
|
||||||
|
done)
|
||||||
|
esac
|
||||||
case "$taglist" in
|
case "$taglist" in
|
||||||
'') ;;
|
'') ;;
|
||||||
?*)
|
?*)
|
||||||
|
|
|
@ -28,21 +28,12 @@ sub andjoin {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub repoconfig {
|
sub repoconfig {
|
||||||
my $fh;
|
my ($val) = qx{git-repo-config --get merge.summary};
|
||||||
my $val;
|
|
||||||
eval {
|
|
||||||
open $fh, '-|', 'git-repo-config', '--get', 'merge.summary'
|
|
||||||
or die "$!";
|
|
||||||
($val) = <$fh>;
|
|
||||||
close $fh;
|
|
||||||
};
|
|
||||||
return $val;
|
return $val;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub current_branch {
|
sub current_branch {
|
||||||
my $fh;
|
my ($bra) = qx{git-symbolic-ref HEAD};
|
||||||
open $fh, '-|', 'git-symbolic-ref', 'HEAD' or die "$!";
|
|
||||||
my ($bra) = <$fh>;
|
|
||||||
chomp($bra);
|
chomp($bra);
|
||||||
$bra =~ s|^refs/heads/||;
|
$bra =~ s|^refs/heads/||;
|
||||||
if ($bra ne 'master') {
|
if ($bra ne 'master') {
|
||||||
|
@ -50,21 +41,17 @@ sub current_branch {
|
||||||
} else {
|
} else {
|
||||||
$bra = "";
|
$bra = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return $bra;
|
return $bra;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub shortlog {
|
sub shortlog {
|
||||||
my ($tip) = @_;
|
my ($tip) = @_;
|
||||||
my ($fh, @result);
|
my @result;
|
||||||
open $fh, '-|', ('git-log', '--topo-order',
|
foreach ( qx{git-log --topo-order --pretty=oneline $tip ^HEAD} ) {
|
||||||
'--pretty=oneline', $tip, '^HEAD')
|
|
||||||
or die "$!";
|
|
||||||
while (<$fh>) {
|
|
||||||
s/^[0-9a-f]{40}\s+//;
|
s/^[0-9a-f]{40}\s+//;
|
||||||
push @result, $_;
|
push @result, $_;
|
||||||
}
|
}
|
||||||
close $fh or die "$!";
|
die "git-log failed\n" if $?;
|
||||||
return @result;
|
return @result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -189,7 +189,7 @@ my @month_names = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
|
||||||
sub show_date {
|
sub show_date {
|
||||||
my ($time, $tz) = @_;
|
my ($time, $tz) = @_;
|
||||||
my $minutes = abs($tz);
|
my $minutes = abs($tz);
|
||||||
$minutes = ($minutes / 100) * 60 + ($minutes % 100);
|
$minutes = int($minutes / 100) * 60 + ($minutes % 100);
|
||||||
if ($tz < 0) {
|
if ($tz < 0) {
|
||||||
$minutes = -$minutes;
|
$minutes = -$minutes;
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,7 @@ case "$#,$common,$no_commit" in
|
||||||
echo "Updating from $head to $1."
|
echo "Updating from $head to $1."
|
||||||
git-update-index --refresh 2>/dev/null
|
git-update-index --refresh 2>/dev/null
|
||||||
new_head=$(git-rev-parse --verify "$1^0") &&
|
new_head=$(git-rev-parse --verify "$1^0") &&
|
||||||
git-read-tree -u -m $head "$new_head" &&
|
git-read-tree -u -v -m $head "$new_head" &&
|
||||||
finish "$new_head" "Fast forward"
|
finish "$new_head" "Fast forward"
|
||||||
dropsave
|
dropsave
|
||||||
exit 0
|
exit 0
|
||||||
|
@ -150,7 +150,7 @@ case "$#,$common,$no_commit" in
|
||||||
|
|
||||||
echo "Trying really trivial in-index merge..."
|
echo "Trying really trivial in-index merge..."
|
||||||
git-update-index --refresh 2>/dev/null
|
git-update-index --refresh 2>/dev/null
|
||||||
if git-read-tree --trivial -m -u $common $head "$1" &&
|
if git-read-tree --trivial -m -u -v $common $head "$1" &&
|
||||||
result_tree=$(git-write-tree)
|
result_tree=$(git-write-tree)
|
||||||
then
|
then
|
||||||
echo "Wonderful."
|
echo "Wonderful."
|
||||||
|
|
|
@ -8,6 +8,7 @@ USAGE='[--all] [--tags] [--force] <repository> [<refspec>...]'
|
||||||
has_all=
|
has_all=
|
||||||
has_force=
|
has_force=
|
||||||
has_exec=
|
has_exec=
|
||||||
|
has_thin=
|
||||||
remote=
|
remote=
|
||||||
do_tags=
|
do_tags=
|
||||||
|
|
||||||
|
@ -22,6 +23,8 @@ do
|
||||||
has_force=--force ;;
|
has_force=--force ;;
|
||||||
--exec=*)
|
--exec=*)
|
||||||
has_exec="$1" ;;
|
has_exec="$1" ;;
|
||||||
|
--thin)
|
||||||
|
has_thin="$1" ;;
|
||||||
-*)
|
-*)
|
||||||
usage ;;
|
usage ;;
|
||||||
*)
|
*)
|
||||||
|
@ -72,6 +75,7 @@ set x "$remote" "$@"; shift
|
||||||
test "$has_all" && set x "$has_all" "$@" && shift
|
test "$has_all" && set x "$has_all" "$@" && shift
|
||||||
test "$has_force" && set x "$has_force" "$@" && shift
|
test "$has_force" && set x "$has_force" "$@" && shift
|
||||||
test "$has_exec" && set x "$has_exec" "$@" && shift
|
test "$has_exec" && set x "$has_exec" "$@" && shift
|
||||||
|
test "$has_thin" && set x "$has_thin" "$@" && shift
|
||||||
|
|
||||||
case "$remote" in
|
case "$remote" in
|
||||||
http://* | https://*)
|
http://* | https://*)
|
||||||
|
|
|
@ -4,24 +4,28 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
USAGE='[--onto <newbase>] <upstream> [<branch>]'
|
USAGE='[--onto <newbase>] <upstream> [<branch>]'
|
||||||
LONG_USAGE='If <branch> is specified, switch to that branch first. Then,
|
LONG_USAGE='git-rebase applies to <upstream> (or optionally to <newbase>) commits
|
||||||
extract commits in the current branch that are not in <upstream>,
|
from <branch> that do not appear in <upstream>. When <branch> is not
|
||||||
and reconstruct the current on top of <upstream>, discarding the original
|
specified it defaults to the current branch (HEAD).
|
||||||
development history. If --onto <newbase> is specified, the history is
|
|
||||||
reconstructed on top of <newbase>, instead of <upstream>. For example,
|
When git-rebase is complete, <branch> will be updated to point to the
|
||||||
while on "topic" branch:
|
newly created line of commit objects, so the previous line will not be
|
||||||
|
accessible unless there are other references to it already.
|
||||||
|
|
||||||
|
Assuming the following history:
|
||||||
|
|
||||||
A---B---C topic
|
A---B---C topic
|
||||||
/
|
/
|
||||||
D---E---F---G master
|
D---E---F---G master
|
||||||
|
|
||||||
$ '"$0"' --onto master~1 master topic
|
The result of the following command:
|
||||||
|
|
||||||
would rewrite the history to look like this:
|
git-rebase --onto master~1 master topic
|
||||||
|
|
||||||
|
would be:
|
||||||
|
|
||||||
A'\''--B'\''--C'\'' topic
|
A'\''--B'\''--C'\'' topic
|
||||||
/
|
/
|
||||||
D---E---F---G master
|
D---E---F---G master
|
||||||
'
|
'
|
||||||
|
|
||||||
|
@ -71,7 +75,7 @@ esac
|
||||||
# The upstream head must be given. Make sure it is valid.
|
# The upstream head must be given. Make sure it is valid.
|
||||||
upstream_name="$1"
|
upstream_name="$1"
|
||||||
upstream=`git rev-parse --verify "${upstream_name}^0"` ||
|
upstream=`git rev-parse --verify "${upstream_name}^0"` ||
|
||||||
die "invalid upsteram $upstream_name"
|
die "invalid upstream $upstream_name"
|
||||||
|
|
||||||
# If a hook exists, give it a chance to interrupt
|
# If a hook exists, give it a chance to interrupt
|
||||||
if test -x "$GIT_DIR/hooks/pre-rebase"
|
if test -x "$GIT_DIR/hooks/pre-rebase"
|
||||||
|
|
|
@ -3,17 +3,20 @@
|
||||||
# Copyright (c) 2005 Linus Torvalds
|
# Copyright (c) 2005 Linus Torvalds
|
||||||
#
|
#
|
||||||
|
|
||||||
USAGE='[-a] [-d] [-l] [-n]'
|
USAGE='[-a] [-d] [-f] [-l] [-n] [-q]'
|
||||||
. git-sh-setup
|
. git-sh-setup
|
||||||
|
|
||||||
no_update_info= all_into_one= remove_redundant= local=
|
no_update_info= all_into_one= remove_redundant=
|
||||||
|
local= quiet= no_reuse_delta=
|
||||||
while case "$#" in 0) break ;; esac
|
while case "$#" in 0) break ;; esac
|
||||||
do
|
do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
-n) no_update_info=t ;;
|
-n) no_update_info=t ;;
|
||||||
-a) all_into_one=t ;;
|
-a) all_into_one=t ;;
|
||||||
-d) remove_redundant=t ;;
|
-d) remove_redundant=t ;;
|
||||||
-l) local=t ;;
|
-q) quiet=-q ;;
|
||||||
|
-f) no_reuse_delta=--no-reuse-delta ;;
|
||||||
|
-l) local=--local ;;
|
||||||
*) usage ;;
|
*) usage ;;
|
||||||
esac
|
esac
|
||||||
shift
|
shift
|
||||||
|
@ -39,9 +42,7 @@ case ",$all_into_one," in
|
||||||
find . -type f \( -name '*.pack' -o -name '*.idx' \) -print`
|
find . -type f \( -name '*.pack' -o -name '*.idx' \) -print`
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
if [ "$local" ]; then
|
pack_objects="$pack_objects $local $quiet $no_reuse_delta"
|
||||||
pack_objects="$pack_objects --local"
|
|
||||||
fi
|
|
||||||
name=$(git-rev-list --objects $rev_list $(git-rev-parse $rev_parse) 2>&1 |
|
name=$(git-rev-list --objects $rev_list $(git-rev-parse $rev_parse) 2>&1 |
|
||||||
git-pack-objects --non-empty $pack_objects .tmp-pack) ||
|
git-pack-objects --non-empty $pack_objects .tmp-pack) ||
|
||||||
exit 1
|
exit 1
|
||||||
|
|
|
@ -131,7 +131,11 @@ sub record_preimage {
|
||||||
sub find_conflict {
|
sub find_conflict {
|
||||||
my $in;
|
my $in;
|
||||||
local $/ = "\0";
|
local $/ = "\0";
|
||||||
open $in, '-|', qw(git ls-files -z -u) or die "$!: ls-files";
|
my $pid = open($in, '-|');
|
||||||
|
die "$!" unless defined $pid;
|
||||||
|
if (!$pid) {
|
||||||
|
exec(qw(git ls-files -z -u)) or die "$!: ls-files";
|
||||||
|
}
|
||||||
my %path = ();
|
my %path = ();
|
||||||
my @path = ();
|
my @path = ();
|
||||||
while (<$in>) {
|
while (<$in>) {
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
#!/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
|
|
@ -59,24 +59,29 @@ my $rc = GetOptions("from=s" => \$from,
|
||||||
|
|
||||||
# Now, let's fill any that aren't set in with defaults:
|
# Now, let's fill any that aren't set in with defaults:
|
||||||
|
|
||||||
open(GITVAR,"-|","git-var","-l")
|
sub gitvar {
|
||||||
or die "Failed to open pipe from git-var: $!";
|
my ($var) = @_;
|
||||||
|
my $fh;
|
||||||
my ($author,$committer);
|
my $pid = open($fh, '-|');
|
||||||
while(<GITVAR>) {
|
die "$!" unless defined $pid;
|
||||||
chomp;
|
if (!$pid) {
|
||||||
my ($var,$data) = split /=/,$_,2;
|
exec('git-var', $var) or die "$!";
|
||||||
my @fields = split /\s+/, $data;
|
}
|
||||||
|
my ($val) = <$fh>;
|
||||||
my $ident = join(" ", @fields[0...(@fields-3)]);
|
close $fh or die "$!";
|
||||||
|
chomp($val);
|
||||||
if ($var eq 'GIT_AUTHOR_IDENT') {
|
return $val;
|
||||||
$author = $ident;
|
|
||||||
} elsif ($var eq 'GIT_COMMITTER_IDENT') {
|
|
||||||
$committer = $ident;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
close(GITVAR);
|
|
||||||
|
sub gitvar_ident {
|
||||||
|
my ($name) = @_;
|
||||||
|
my $val = gitvar($name);
|
||||||
|
my @field = split(/\s+/, $val);
|
||||||
|
return join(' ', @field[0...(@field-3)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
my ($author) = gitvar_ident('GIT_AUTHOR_IDENT');
|
||||||
|
my ($committer) = gitvar_ident('GIT_COMMITTER_IDENT');
|
||||||
|
|
||||||
my $prompting = 0;
|
my $prompting = 0;
|
||||||
if (!defined $from) {
|
if (!defined $from) {
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
# The head revision is on branch "origin" by default.
|
# The head revision is on branch "origin" by default.
|
||||||
# You can change that with the '-o' option.
|
# You can change that with the '-o' option.
|
||||||
|
|
||||||
require 5.008; # for shell-safe open("-|",LIST)
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use Getopt::Std;
|
use Getopt::Std;
|
||||||
|
@ -322,8 +321,12 @@ sub get_file($$$) {
|
||||||
return undef unless defined $name;
|
return undef unless defined $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
open my $F, '-|', "git-hash-object", "-w", $name
|
my $pid = open(my $F, '-|');
|
||||||
|
die $! unless defined $pid;
|
||||||
|
if (!$pid) {
|
||||||
|
exec("git-hash-object", "-w", $name)
|
||||||
or die "Cannot create object: $!\n";
|
or die "Cannot create object: $!\n";
|
||||||
|
}
|
||||||
my $sha = <$F>;
|
my $sha = <$F>;
|
||||||
chomp $sha;
|
chomp $sha;
|
||||||
close $F;
|
close $F;
|
||||||
|
@ -398,7 +401,12 @@ sub copy_path($$$$$$$$) {
|
||||||
$srcpath =~ s#/*$#/#;
|
$srcpath =~ s#/*$#/#;
|
||||||
}
|
}
|
||||||
|
|
||||||
open my $f,"-|","git-ls-tree","-r","-z",$gitrev,$srcpath;
|
my $pid = open my $f,'-|';
|
||||||
|
die $! unless defined $pid;
|
||||||
|
if (!$pid) {
|
||||||
|
exec("git-ls-tree","-r","-z",$gitrev,$srcpath)
|
||||||
|
or die $!;
|
||||||
|
}
|
||||||
local $/ = "\0";
|
local $/ = "\0";
|
||||||
while(<$f>) {
|
while(<$f>) {
|
||||||
chomp;
|
chomp;
|
||||||
|
@ -554,7 +562,11 @@ sub commit {
|
||||||
@o1 = @old;
|
@o1 = @old;
|
||||||
@old = ();
|
@old = ();
|
||||||
}
|
}
|
||||||
open my $F, "-|", "git-ls-files", "-z", @o1 or die $!;
|
my $pid = open my $F, "-|";
|
||||||
|
die "$!" unless defined $pid;
|
||||||
|
if (!$pid) {
|
||||||
|
exec("git-ls-files", "-z", @o1) or die $!;
|
||||||
|
}
|
||||||
@o1 = ();
|
@o1 = ();
|
||||||
local $/ = "\0";
|
local $/ = "\0";
|
||||||
while(<$F>) {
|
while(<$F>) {
|
||||||
|
|
175
git.c
175
git.c
|
@ -12,6 +12,10 @@
|
||||||
#include "git-compat-util.h"
|
#include "git-compat-util.h"
|
||||||
#include "exec_cmd.h"
|
#include "exec_cmd.h"
|
||||||
|
|
||||||
|
#include "cache.h"
|
||||||
|
#include "commit.h"
|
||||||
|
#include "revision.h"
|
||||||
|
|
||||||
#ifndef PATH_MAX
|
#ifndef PATH_MAX
|
||||||
# define PATH_MAX 4096
|
# define PATH_MAX 4096
|
||||||
#endif
|
#endif
|
||||||
|
@ -230,62 +234,161 @@ static void show_man_page(char *git_cmd)
|
||||||
execlp("man", "man", page, NULL);
|
execlp("man", "man", page, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cmd_version(int argc, char **argv, char **envp)
|
||||||
|
{
|
||||||
|
printf("git version %s\n", GIT_VERSION);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_help(int argc, char **argv, char **envp)
|
||||||
|
{
|
||||||
|
char *help_cmd = argv[1];
|
||||||
|
if (!help_cmd)
|
||||||
|
cmd_usage(git_exec_path(), NULL);
|
||||||
|
show_man_page(help_cmd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LOGSIZE (65536)
|
||||||
|
|
||||||
|
static int cmd_log(int argc, char **argv, char **envp)
|
||||||
|
{
|
||||||
|
struct rev_info rev;
|
||||||
|
struct commit *commit;
|
||||||
|
char *buf = xmalloc(LOGSIZE);
|
||||||
|
|
||||||
|
argc = setup_revisions(argc, argv, &rev, "HEAD");
|
||||||
|
prepare_revision_walk(&rev);
|
||||||
|
setup_pager();
|
||||||
|
while ((commit = get_revision(&rev)) != NULL) {
|
||||||
|
pretty_print_commit(CMIT_FMT_DEFAULT, commit, ~0, buf, LOGSIZE, 18);
|
||||||
|
printf("%s\n", buf);
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
|
||||||
|
|
||||||
|
static void handle_internal_command(int argc, char **argv, char **envp)
|
||||||
|
{
|
||||||
|
const char *cmd = argv[0];
|
||||||
|
static struct cmd_struct {
|
||||||
|
const char *cmd;
|
||||||
|
int (*fn)(int, char **, char **);
|
||||||
|
} commands[] = {
|
||||||
|
{ "version", cmd_version },
|
||||||
|
{ "help", cmd_help },
|
||||||
|
{ "log", cmd_log },
|
||||||
|
};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(commands); i++) {
|
||||||
|
struct cmd_struct *p = commands+i;
|
||||||
|
if (strcmp(p->cmd, cmd))
|
||||||
|
continue;
|
||||||
|
exit(p->fn(argc, argv, envp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv, char **envp)
|
int main(int argc, char **argv, char **envp)
|
||||||
{
|
{
|
||||||
|
char *cmd = argv[0];
|
||||||
|
char *slash = strrchr(cmd, '/');
|
||||||
char git_command[PATH_MAX + 1];
|
char git_command[PATH_MAX + 1];
|
||||||
char wd[PATH_MAX + 1];
|
const char *exec_path = NULL;
|
||||||
int i, show_help = 0;
|
|
||||||
const char *exec_path;
|
|
||||||
|
|
||||||
getcwd(wd, PATH_MAX);
|
/*
|
||||||
|
* Take the basename of argv[0] as the command
|
||||||
|
* name, and the dirname as the default exec_path
|
||||||
|
* if it's an absolute path and we don't have
|
||||||
|
* anything better.
|
||||||
|
*/
|
||||||
|
if (slash) {
|
||||||
|
*slash++ = 0;
|
||||||
|
if (*cmd == '/')
|
||||||
|
exec_path = cmd;
|
||||||
|
cmd = slash;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 1; i < argc; i++) {
|
/*
|
||||||
char *arg = argv[i];
|
* "git-xxxx" is the same as "git xxxx", but we obviously:
|
||||||
|
*
|
||||||
|
* - cannot take flags in between the "git" and the "xxxx".
|
||||||
|
* - cannot execute it externally (since it would just do
|
||||||
|
* the same thing over again)
|
||||||
|
*
|
||||||
|
* So we just directly call the internal command handler, and
|
||||||
|
* die if that one cannot handle it.
|
||||||
|
*/
|
||||||
|
if (!strncmp(cmd, "git-", 4)) {
|
||||||
|
cmd += 4;
|
||||||
|
argv[0] = cmd;
|
||||||
|
handle_internal_command(argc, argv, envp);
|
||||||
|
die("cannot handle %s internally", cmd);
|
||||||
|
}
|
||||||
|
|
||||||
if (!strcmp(arg, "help")) {
|
/* Default command: "help" */
|
||||||
show_help = 1;
|
cmd = "help";
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strncmp(arg, "--", 2))
|
/* Look for flags.. */
|
||||||
|
while (argc > 1) {
|
||||||
|
cmd = *++argv;
|
||||||
|
argc--;
|
||||||
|
|
||||||
|
if (strncmp(cmd, "--", 2))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
arg += 2;
|
cmd += 2;
|
||||||
|
|
||||||
if (!strncmp(arg, "exec-path", 9)) {
|
/*
|
||||||
arg += 9;
|
* For legacy reasons, the "version" and "help"
|
||||||
if (*arg == '=') {
|
* commands can be written with "--" prepended
|
||||||
exec_path = arg + 1;
|
* to make them look like flags.
|
||||||
git_set_exec_path(exec_path);
|
*/
|
||||||
} else {
|
if (!strcmp(cmd, "help"))
|
||||||
puts(git_exec_path());
|
break;
|
||||||
exit(0);
|
if (!strcmp(cmd, "version"))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check remaining flags (which by now must be
|
||||||
|
* "--exec-path", but maybe we will accept
|
||||||
|
* other arguments some day)
|
||||||
|
*/
|
||||||
|
if (!strncmp(cmd, "exec-path", 9)) {
|
||||||
|
cmd += 9;
|
||||||
|
if (*cmd == '=') {
|
||||||
|
git_set_exec_path(cmd + 1);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
puts(git_exec_path());
|
||||||
else if (!strcmp(arg, "version")) {
|
|
||||||
printf("git version %s\n", GIT_VERSION);
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
else if (!strcmp(arg, "help"))
|
cmd_usage(NULL, NULL);
|
||||||
show_help = 1;
|
|
||||||
else if (!show_help)
|
|
||||||
cmd_usage(NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i >= argc || show_help) {
|
|
||||||
if (i >= argc)
|
|
||||||
cmd_usage(git_exec_path(), NULL);
|
|
||||||
|
|
||||||
show_man_page(argv[i]);
|
|
||||||
}
|
}
|
||||||
|
argv[0] = cmd;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We search for git commands in the following order:
|
||||||
|
* - git_exec_path()
|
||||||
|
* - the path of the "git" command if we could find it
|
||||||
|
* in $0
|
||||||
|
* - the regular PATH.
|
||||||
|
*/
|
||||||
|
if (exec_path)
|
||||||
|
prepend_to_path(exec_path, strlen(exec_path));
|
||||||
exec_path = git_exec_path();
|
exec_path = git_exec_path();
|
||||||
prepend_to_path(exec_path, strlen(exec_path));
|
prepend_to_path(exec_path, strlen(exec_path));
|
||||||
|
|
||||||
execv_git_cmd(argv + i);
|
/* See if it's an internal command */
|
||||||
|
handle_internal_command(argc, argv, envp);
|
||||||
|
|
||||||
|
/* .. then try the external ones */
|
||||||
|
execv_git_cmd(argv);
|
||||||
|
|
||||||
if (errno == ENOENT)
|
if (errno == ENOENT)
|
||||||
cmd_usage(exec_path, "'%s' is not a git-command", argv[i]);
|
cmd_usage(exec_path, "'%s' is not a git-command", cmd);
|
||||||
|
|
||||||
fprintf(stderr, "Failed to run command '%s': %s\n",
|
fprintf(stderr, "Failed to run command '%s': %s\n",
|
||||||
git_command, strerror(errno));
|
git_command, strerror(errno));
|
||||||
|
|
82
gitk
82
gitk
|
@ -1936,7 +1936,7 @@ proc findfiles {} {
|
||||||
global selectedline numcommits lineid ctext
|
global selectedline numcommits lineid ctext
|
||||||
global ffileline finddidsel parents nparents
|
global ffileline finddidsel parents nparents
|
||||||
global findinprogress findstartline findinsertpos
|
global findinprogress findstartline findinsertpos
|
||||||
global treediffs fdiffids fdiffsneeded fdiffpos
|
global treediffs fdiffid fdiffsneeded fdiffpos
|
||||||
global findmergefiles
|
global findmergefiles
|
||||||
|
|
||||||
if {$numcommits == 0} return
|
if {$numcommits == 0} return
|
||||||
|
@ -1953,11 +1953,9 @@ proc findfiles {} {
|
||||||
while 1 {
|
while 1 {
|
||||||
set id $lineid($l)
|
set id $lineid($l)
|
||||||
if {$findmergefiles || $nparents($id) == 1} {
|
if {$findmergefiles || $nparents($id) == 1} {
|
||||||
foreach p $parents($id) {
|
if {![info exists treediffs($id)]} {
|
||||||
if {![info exists treediffs([list $id $p])]} {
|
append diffsneeded "$id\n"
|
||||||
append diffsneeded "$id $p\n"
|
lappend fdiffsneeded $id
|
||||||
lappend fdiffsneeded [list $id $p]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if {[incr l] >= $numcommits} {
|
if {[incr l] >= $numcommits} {
|
||||||
|
@ -1974,7 +1972,7 @@ proc findfiles {} {
|
||||||
error_popup "Error starting search process: $err"
|
error_popup "Error starting search process: $err"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
catch {unset fdiffids}
|
catch {unset fdiffid}
|
||||||
set fdiffpos 0
|
set fdiffpos 0
|
||||||
fconfigure $df -blocking 0
|
fconfigure $df -blocking 0
|
||||||
fileevent $df readable [list readfilediffs $df]
|
fileevent $df readable [list readfilediffs $df]
|
||||||
|
@ -1983,16 +1981,15 @@ proc findfiles {} {
|
||||||
set finddidsel 0
|
set finddidsel 0
|
||||||
set findinsertpos end
|
set findinsertpos end
|
||||||
set id $lineid($l)
|
set id $lineid($l)
|
||||||
set p [lindex $parents($id) 0]
|
|
||||||
. config -cursor watch
|
. config -cursor watch
|
||||||
settextcursor watch
|
settextcursor watch
|
||||||
set findinprogress 1
|
set findinprogress 1
|
||||||
findcont [list $id $p]
|
findcont $id
|
||||||
update
|
update
|
||||||
}
|
}
|
||||||
|
|
||||||
proc readfilediffs {df} {
|
proc readfilediffs {df} {
|
||||||
global findids fdiffids fdiffs
|
global findid fdiffid fdiffs
|
||||||
|
|
||||||
set n [gets $df line]
|
set n [gets $df line]
|
||||||
if {$n < 0} {
|
if {$n < 0} {
|
||||||
|
@ -2002,19 +1999,19 @@ proc readfilediffs {df} {
|
||||||
stopfindproc
|
stopfindproc
|
||||||
bell
|
bell
|
||||||
error_popup "Error in git-diff-tree: $err"
|
error_popup "Error in git-diff-tree: $err"
|
||||||
} elseif {[info exists findids]} {
|
} elseif {[info exists findid]} {
|
||||||
set ids $findids
|
set id $findid
|
||||||
stopfindproc
|
stopfindproc
|
||||||
bell
|
bell
|
||||||
error_popup "Couldn't find diffs for {$ids}"
|
error_popup "Couldn't find diffs for $id"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if {[regexp {^([0-9a-f]{40}) \(from ([0-9a-f]{40})\)} $line match id p]} {
|
if {[regexp {^([0-9a-f]{40})$} $line match id]} {
|
||||||
# start of a new string of diffs
|
# start of a new string of diffs
|
||||||
donefilediff
|
donefilediff
|
||||||
set fdiffids [list $id $p]
|
set fdiffid $id
|
||||||
set fdiffs {}
|
set fdiffs {}
|
||||||
} elseif {[string match ":*" $line]} {
|
} elseif {[string match ":*" $line]} {
|
||||||
lappend fdiffs [lindex $line 5]
|
lappend fdiffs [lindex $line 5]
|
||||||
|
@ -2022,53 +2019,50 @@ proc readfilediffs {df} {
|
||||||
}
|
}
|
||||||
|
|
||||||
proc donefilediff {} {
|
proc donefilediff {} {
|
||||||
global fdiffids fdiffs treediffs findids
|
global fdiffid fdiffs treediffs findid
|
||||||
global fdiffsneeded fdiffpos
|
global fdiffsneeded fdiffpos
|
||||||
|
|
||||||
if {[info exists fdiffids]} {
|
if {[info exists fdiffid]} {
|
||||||
while {[lindex $fdiffsneeded $fdiffpos] ne $fdiffids
|
while {[lindex $fdiffsneeded $fdiffpos] ne $fdiffid
|
||||||
&& $fdiffpos < [llength $fdiffsneeded]} {
|
&& $fdiffpos < [llength $fdiffsneeded]} {
|
||||||
# git-diff-tree doesn't output anything for a commit
|
# git-diff-tree doesn't output anything for a commit
|
||||||
# which doesn't change anything
|
# which doesn't change anything
|
||||||
set nullids [lindex $fdiffsneeded $fdiffpos]
|
set nullid [lindex $fdiffsneeded $fdiffpos]
|
||||||
set treediffs($nullids) {}
|
set treediffs($nullid) {}
|
||||||
if {[info exists findids] && $nullids eq $findids} {
|
if {[info exists findid] && $nullid eq $findid} {
|
||||||
unset findids
|
unset findid
|
||||||
findcont $nullids
|
findcont $nullid
|
||||||
}
|
}
|
||||||
incr fdiffpos
|
incr fdiffpos
|
||||||
}
|
}
|
||||||
incr fdiffpos
|
incr fdiffpos
|
||||||
|
|
||||||
if {![info exists treediffs($fdiffids)]} {
|
if {![info exists treediffs($fdiffid)]} {
|
||||||
set treediffs($fdiffids) $fdiffs
|
set treediffs($fdiffid) $fdiffs
|
||||||
}
|
}
|
||||||
if {[info exists findids] && $fdiffids eq $findids} {
|
if {[info exists findid] && $fdiffid eq $findid} {
|
||||||
unset findids
|
unset findid
|
||||||
findcont $fdiffids
|
findcont $fdiffid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proc findcont {ids} {
|
proc findcont {id} {
|
||||||
global findids treediffs parents nparents
|
global findid treediffs parents nparents
|
||||||
global ffileline findstartline finddidsel
|
global ffileline findstartline finddidsel
|
||||||
global lineid numcommits matchinglines findinprogress
|
global lineid numcommits matchinglines findinprogress
|
||||||
global findmergefiles
|
global findmergefiles
|
||||||
|
|
||||||
set id [lindex $ids 0]
|
|
||||||
set p [lindex $ids 1]
|
|
||||||
set pi [lsearch -exact $parents($id) $p]
|
|
||||||
set l $ffileline
|
set l $ffileline
|
||||||
while 1 {
|
while 1 {
|
||||||
if {$findmergefiles || $nparents($id) == 1} {
|
if {$findmergefiles || $nparents($id) == 1} {
|
||||||
if {![info exists treediffs($ids)]} {
|
if {![info exists treediffs($id)]} {
|
||||||
set findids $ids
|
set findid $id
|
||||||
set ffileline $l
|
set ffileline $l
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
set doesmatch 0
|
set doesmatch 0
|
||||||
foreach f $treediffs($ids) {
|
foreach f $treediffs($id) {
|
||||||
set x [findmatches $f]
|
set x [findmatches $f]
|
||||||
if {$x != {}} {
|
if {$x != {}} {
|
||||||
set doesmatch 1
|
set doesmatch 1
|
||||||
|
@ -2077,21 +2071,13 @@ proc findcont {ids} {
|
||||||
}
|
}
|
||||||
if {$doesmatch} {
|
if {$doesmatch} {
|
||||||
insertmatch $l $id
|
insertmatch $l $id
|
||||||
set pi $nparents($id)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
set pi $nparents($id)
|
|
||||||
}
|
}
|
||||||
if {[incr pi] >= $nparents($id)} {
|
if {[incr l] >= $numcommits} {
|
||||||
set pi 0
|
set l 0
|
||||||
if {[incr l] >= $numcommits} {
|
|
||||||
set l 0
|
|
||||||
}
|
|
||||||
if {$l == $findstartline} break
|
|
||||||
set id $lineid($l)
|
|
||||||
}
|
}
|
||||||
set p [lindex $parents($id) $pi]
|
if {$l == $findstartline} break
|
||||||
set ids [list $id $p]
|
set id $lineid($l)
|
||||||
}
|
}
|
||||||
stopfindproc
|
stopfindproc
|
||||||
if {!$finddidsel} {
|
if {!$finddidsel} {
|
||||||
|
|
|
@ -130,7 +130,7 @@ static void start_object_request(struct object_request *obj_req)
|
||||||
|
|
||||||
if (obj_req->local < 0) {
|
if (obj_req->local < 0) {
|
||||||
obj_req->state = ABORTED;
|
obj_req->state = ABORTED;
|
||||||
error("Couldn't create temporary file %s for %s: %s\n",
|
error("Couldn't create temporary file %s for %s: %s",
|
||||||
obj_req->tmpfile, obj_req->filename, strerror(errno));
|
obj_req->tmpfile, obj_req->filename, strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -830,9 +830,9 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
|
||||||
obj_req->errorstr, obj_req->curl_result,
|
obj_req->errorstr, obj_req->curl_result,
|
||||||
obj_req->http_code, hex);
|
obj_req->http_code, hex);
|
||||||
} else if (obj_req->zret != Z_STREAM_END) {
|
} else if (obj_req->zret != Z_STREAM_END) {
|
||||||
ret = error("File %s (%s) corrupt\n", hex, obj_req->url);
|
ret = error("File %s (%s) corrupt", hex, obj_req->url);
|
||||||
} else if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) {
|
} else if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) {
|
||||||
ret = error("File %s has bad hash\n", hex);
|
ret = error("File %s has bad hash", hex);
|
||||||
} else if (obj_req->rename < 0) {
|
} else if (obj_req->rename < 0) {
|
||||||
ret = error("unable to write sha1 filename %s",
|
ret = error("unable to write sha1 filename %s",
|
||||||
obj_req->filename);
|
obj_req->filename);
|
||||||
|
@ -854,7 +854,7 @@ int fetch(unsigned char *sha1)
|
||||||
fetch_alternates(alt->base);
|
fetch_alternates(alt->base);
|
||||||
altbase = altbase->next;
|
altbase = altbase->next;
|
||||||
}
|
}
|
||||||
return error("Unable to find %s under %s\n", sha1_to_hex(sha1),
|
return error("Unable to find %s under %s", sha1_to_hex(sha1),
|
||||||
alt->base);
|
alt->base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
34
ls-files.c
34
ls-files.c
|
@ -20,6 +20,7 @@ static int show_unmerged = 0;
|
||||||
static int show_modified = 0;
|
static int show_modified = 0;
|
||||||
static int show_killed = 0;
|
static int show_killed = 0;
|
||||||
static int show_other_directories = 0;
|
static int show_other_directories = 0;
|
||||||
|
static int show_valid_bit = 0;
|
||||||
static int line_terminator = '\n';
|
static int line_terminator = '\n';
|
||||||
|
|
||||||
static int prefix_len = 0, prefix_offset = 0;
|
static int prefix_len = 0, prefix_offset = 0;
|
||||||
|
@ -278,8 +279,11 @@ static void read_directory(const char *path, const char *base, int baselen)
|
||||||
continue;
|
continue;
|
||||||
len = strlen(de->d_name);
|
len = strlen(de->d_name);
|
||||||
memcpy(fullname + baselen, de->d_name, len+1);
|
memcpy(fullname + baselen, de->d_name, len+1);
|
||||||
if (excluded(fullname) != show_ignored)
|
if (excluded(fullname) != show_ignored) {
|
||||||
continue;
|
if (!show_ignored || DTYPE(de) != DT_DIR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (DTYPE(de)) {
|
switch (DTYPE(de)) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
@ -457,6 +461,23 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
|
||||||
if (pathspec && !match(pathspec, ps_matched, ce->name, len))
|
if (pathspec && !match(pathspec, ps_matched, ce->name, len))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (tag && *tag && show_valid_bit &&
|
||||||
|
(ce->ce_flags & htons(CE_VALID))) {
|
||||||
|
static char alttag[4];
|
||||||
|
memcpy(alttag, tag, 3);
|
||||||
|
if (isalpha(tag[0]))
|
||||||
|
alttag[0] = tolower(tag[0]);
|
||||||
|
else if (tag[0] == '?')
|
||||||
|
alttag[0] = '!';
|
||||||
|
else {
|
||||||
|
alttag[0] = 'v';
|
||||||
|
alttag[1] = tag[0];
|
||||||
|
alttag[2] = ' ';
|
||||||
|
alttag[3] = 0;
|
||||||
|
}
|
||||||
|
tag = alttag;
|
||||||
|
}
|
||||||
|
|
||||||
if (!show_stage) {
|
if (!show_stage) {
|
||||||
fputs(tag, stdout);
|
fputs(tag, stdout);
|
||||||
write_name_quoted("", 0, ce->name + offset,
|
write_name_quoted("", 0, ce->name + offset,
|
||||||
|
@ -533,7 +554,7 @@ static void show_files(void)
|
||||||
err = lstat(ce->name, &st);
|
err = lstat(ce->name, &st);
|
||||||
if (show_deleted && err)
|
if (show_deleted && err)
|
||||||
show_ce_entry(tag_removed, ce);
|
show_ce_entry(tag_removed, ce);
|
||||||
if (show_modified && ce_modified(ce, &st))
|
if (show_modified && ce_modified(ce, &st, 0))
|
||||||
show_ce_entry(tag_modified, ce);
|
show_ce_entry(tag_modified, ce);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -606,7 +627,7 @@ static void verify_pathspec(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char ls_files_usage[] =
|
static const char ls_files_usage[] =
|
||||||
"git-ls-files [-z] [-t] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
|
"git-ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
|
||||||
"[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
|
"[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
|
||||||
"[ --exclude-per-directory=<filename> ] [--full-name] [--] [<file>]*";
|
"[ --exclude-per-directory=<filename> ] [--full-name] [--] [<file>]*";
|
||||||
|
|
||||||
|
@ -631,13 +652,15 @@ int main(int argc, const char **argv)
|
||||||
line_terminator = 0;
|
line_terminator = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(arg, "-t")) {
|
if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) {
|
||||||
tag_cached = "H ";
|
tag_cached = "H ";
|
||||||
tag_unmerged = "M ";
|
tag_unmerged = "M ";
|
||||||
tag_removed = "R ";
|
tag_removed = "R ";
|
||||||
tag_modified = "C ";
|
tag_modified = "C ";
|
||||||
tag_other = "? ";
|
tag_other = "? ";
|
||||||
tag_killed = "K ";
|
tag_killed = "K ";
|
||||||
|
if (arg[1] == 'v')
|
||||||
|
show_valid_bit = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) {
|
if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) {
|
||||||
|
@ -758,6 +781,7 @@ int main(int argc, const char **argv)
|
||||||
continue;
|
continue;
|
||||||
error("pathspec '%s' did not match any.",
|
error("pathspec '%s' did not match any.",
|
||||||
pathspec[num] + prefix_offset);
|
pathspec[num] + prefix_offset);
|
||||||
|
errors++;
|
||||||
}
|
}
|
||||||
return errors ? 1 : 0;
|
return errors ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
801
pack-objects.c
801
pack-objects.c
File diff suppressed because it is too large
Load Diff
|
@ -45,7 +45,7 @@ static inline void llist_item_put(struct llist_item *item)
|
||||||
free_nodes = item;
|
free_nodes = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct llist_item *llist_item_get()
|
static inline struct llist_item *llist_item_get(void)
|
||||||
{
|
{
|
||||||
struct llist_item *new;
|
struct llist_item *new;
|
||||||
if ( free_nodes ) {
|
if ( free_nodes ) {
|
||||||
|
@ -275,7 +275,7 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pll_free(struct pll *l)
|
static void pll_free(struct pll *l)
|
||||||
{
|
{
|
||||||
struct pll *old;
|
struct pll *old;
|
||||||
struct pack_list *opl;
|
struct pack_list *opl;
|
||||||
|
|
4
pack.h
4
pack.h
|
@ -29,5 +29,7 @@ struct pack_header {
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int verify_pack(struct packed_git *, int);
|
extern int verify_pack(struct packed_git *, int);
|
||||||
|
extern int check_reuse_pack_delta(struct packed_git *, unsigned long,
|
||||||
|
unsigned char *, unsigned long *,
|
||||||
|
enum object_type *);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
#include "cache.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is split up from the rest of git so that we might do
|
||||||
|
* something different on Windows, for example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void run_pager(void)
|
||||||
|
{
|
||||||
|
const char *prog = getenv("PAGER");
|
||||||
|
if (!prog)
|
||||||
|
prog = "less";
|
||||||
|
setenv("LESS", "-S", 0);
|
||||||
|
execlp(prog, prog, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_pager(void)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
int fd[2];
|
||||||
|
|
||||||
|
if (!isatty(1))
|
||||||
|
return;
|
||||||
|
if (pipe(fd) < 0)
|
||||||
|
return;
|
||||||
|
pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
close(fd[0]);
|
||||||
|
close(fd[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return in the child */
|
||||||
|
if (!pid) {
|
||||||
|
dup2(fd[1], 1);
|
||||||
|
close(fd[0]);
|
||||||
|
close(fd[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The original process turns into the PAGER */
|
||||||
|
dup2(fd[0], 0);
|
||||||
|
close(fd[0]);
|
||||||
|
close(fd[1]);
|
||||||
|
|
||||||
|
run_pager();
|
||||||
|
exit(255);
|
||||||
|
}
|
28
read-cache.c
28
read-cache.c
|
@ -27,6 +27,9 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
|
||||||
ce->ce_uid = htonl(st->st_uid);
|
ce->ce_uid = htonl(st->st_uid);
|
||||||
ce->ce_gid = htonl(st->st_gid);
|
ce->ce_gid = htonl(st->st_gid);
|
||||||
ce->ce_size = htonl(st->st_size);
|
ce->ce_size = htonl(st->st_size);
|
||||||
|
|
||||||
|
if (assume_unchanged)
|
||||||
|
ce->ce_flags |= htons(CE_VALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ce_compare_data(struct cache_entry *ce, struct stat *st)
|
static int ce_compare_data(struct cache_entry *ce, struct stat *st)
|
||||||
|
@ -146,9 +149,18 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ce_match_stat(struct cache_entry *ce, struct stat *st)
|
int ce_match_stat(struct cache_entry *ce, struct stat *st, int ignore_valid)
|
||||||
{
|
{
|
||||||
unsigned int changed = ce_match_stat_basic(ce, st);
|
unsigned int changed;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it's marked as always valid in the index, it's
|
||||||
|
* valid whatever the checked-out copy says.
|
||||||
|
*/
|
||||||
|
if (!ignore_valid && (ce->ce_flags & htons(CE_VALID)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
changed = ce_match_stat_basic(ce, st);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Within 1 second of this sequence:
|
* Within 1 second of this sequence:
|
||||||
|
@ -164,7 +176,7 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st)
|
||||||
* effectively mean we can make at most one commit per second,
|
* effectively mean we can make at most one commit per second,
|
||||||
* which is not acceptable. Instead, we check cache entries
|
* which is not acceptable. Instead, we check cache entries
|
||||||
* whose mtime are the same as the index file timestamp more
|
* whose mtime are the same as the index file timestamp more
|
||||||
* careful than others.
|
* carefully than others.
|
||||||
*/
|
*/
|
||||||
if (!changed &&
|
if (!changed &&
|
||||||
index_file_timestamp &&
|
index_file_timestamp &&
|
||||||
|
@ -174,10 +186,10 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st)
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ce_modified(struct cache_entry *ce, struct stat *st)
|
int ce_modified(struct cache_entry *ce, struct stat *st, int really)
|
||||||
{
|
{
|
||||||
int changed, changed_fs;
|
int changed, changed_fs;
|
||||||
changed = ce_match_stat(ce, st);
|
changed = ce_match_stat(ce, st, really);
|
||||||
if (!changed)
|
if (!changed)
|
||||||
return 0;
|
return 0;
|
||||||
/*
|
/*
|
||||||
|
@ -233,6 +245,11 @@ int cache_name_compare(const char *name1, int flags1, const char *name2, int fla
|
||||||
return -1;
|
return -1;
|
||||||
if (len1 > len2)
|
if (len1 > len2)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
/* Compare stages */
|
||||||
|
flags1 &= CE_STAGEMASK;
|
||||||
|
flags2 &= CE_STAGEMASK;
|
||||||
|
|
||||||
if (flags1 < flags2)
|
if (flags1 < flags2)
|
||||||
return -1;
|
return -1;
|
||||||
if (flags1 > flags2)
|
if (flags1 > flags2)
|
||||||
|
@ -430,6 +447,7 @@ int add_cache_entry(struct cache_entry *ce, int option)
|
||||||
int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
|
int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
|
||||||
int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE;
|
int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE;
|
||||||
int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
|
int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
|
||||||
|
|
||||||
pos = cache_name_pos(ce->name, ntohs(ce->ce_flags));
|
pos = cache_name_pos(ce->name, ntohs(ce->ce_flags));
|
||||||
|
|
||||||
/* existing match? Just replace it. */
|
/* existing match? Just replace it. */
|
||||||
|
|
66
read-tree.c
66
read-tree.c
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
#include "object.h"
|
#include "object.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
static int merge = 0;
|
static int merge = 0;
|
||||||
static int update = 0;
|
static int update = 0;
|
||||||
|
@ -16,6 +18,8 @@ static int index_only = 0;
|
||||||
static int nontrivial_merge = 0;
|
static int nontrivial_merge = 0;
|
||||||
static int trivial_merges_only = 0;
|
static int trivial_merges_only = 0;
|
||||||
static int aggressive = 0;
|
static int aggressive = 0;
|
||||||
|
static int verbose_update = 0;
|
||||||
|
static volatile int progress_update = 0;
|
||||||
|
|
||||||
static int head_idx = -1;
|
static int head_idx = -1;
|
||||||
static int merge_size = 0;
|
static int merge_size = 0;
|
||||||
|
@ -267,6 +271,12 @@ static void unlink_entry(char *name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void progress_interval(int signum)
|
||||||
|
{
|
||||||
|
signal(SIGALRM, progress_interval);
|
||||||
|
progress_update = 1;
|
||||||
|
}
|
||||||
|
|
||||||
static void check_updates(struct cache_entry **src, int nr)
|
static void check_updates(struct cache_entry **src, int nr)
|
||||||
{
|
{
|
||||||
static struct checkout state = {
|
static struct checkout state = {
|
||||||
|
@ -276,8 +286,49 @@ static void check_updates(struct cache_entry **src, int nr)
|
||||||
.refresh_cache = 1,
|
.refresh_cache = 1,
|
||||||
};
|
};
|
||||||
unsigned short mask = htons(CE_UPDATE);
|
unsigned short mask = htons(CE_UPDATE);
|
||||||
|
unsigned last_percent = 200, cnt = 0, total = 0;
|
||||||
|
|
||||||
|
if (update && verbose_update) {
|
||||||
|
struct itimerval v;
|
||||||
|
|
||||||
|
for (total = cnt = 0; cnt < nr; cnt++) {
|
||||||
|
struct cache_entry *ce = src[cnt];
|
||||||
|
if (!ce->ce_mode || ce->ce_flags & mask)
|
||||||
|
total++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't bother doing this for very small updates */
|
||||||
|
if (total < 250)
|
||||||
|
total = 0;
|
||||||
|
|
||||||
|
if (total) {
|
||||||
|
v.it_interval.tv_sec = 1;
|
||||||
|
v.it_interval.tv_usec = 0;
|
||||||
|
v.it_value = v.it_interval;
|
||||||
|
signal(SIGALRM, progress_interval);
|
||||||
|
setitimer(ITIMER_REAL, &v, NULL);
|
||||||
|
fprintf(stderr, "Checking files out...\n");
|
||||||
|
progress_update = 1;
|
||||||
|
}
|
||||||
|
cnt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
while (nr--) {
|
while (nr--) {
|
||||||
struct cache_entry *ce = *src++;
|
struct cache_entry *ce = *src++;
|
||||||
|
|
||||||
|
if (total) {
|
||||||
|
if (!ce->ce_mode || ce->ce_flags & mask) {
|
||||||
|
unsigned percent;
|
||||||
|
cnt++;
|
||||||
|
percent = (cnt * 100) / total;
|
||||||
|
if (percent != last_percent ||
|
||||||
|
progress_update) {
|
||||||
|
fprintf(stderr, "%4u%% (%u/%u) done\r",
|
||||||
|
percent, cnt, total);
|
||||||
|
last_percent = percent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!ce->ce_mode) {
|
if (!ce->ce_mode) {
|
||||||
if (update)
|
if (update)
|
||||||
unlink_entry(ce->name);
|
unlink_entry(ce->name);
|
||||||
|
@ -289,6 +340,10 @@ static void check_updates(struct cache_entry **src, int nr)
|
||||||
checkout_entry(ce, &state);
|
checkout_entry(ce, &state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (total) {
|
||||||
|
fputc('\n', stderr);
|
||||||
|
signal(SIGALRM, SIG_IGN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unpack_trees(merge_fn_t fn)
|
static int unpack_trees(merge_fn_t fn)
|
||||||
|
@ -349,7 +404,7 @@ static void verify_uptodate(struct cache_entry *ce)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!lstat(ce->name, &st)) {
|
if (!lstat(ce->name, &st)) {
|
||||||
unsigned changed = ce_match_stat(ce, &st);
|
unsigned changed = ce_match_stat(ce, &st, 1);
|
||||||
if (!changed)
|
if (!changed)
|
||||||
return;
|
return;
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
@ -564,7 +619,7 @@ static int twoway_merge(struct cache_entry **src)
|
||||||
struct cache_entry *oldtree = src[1], *newtree = src[2];
|
struct cache_entry *oldtree = src[1], *newtree = src[2];
|
||||||
|
|
||||||
if (merge_size != 2)
|
if (merge_size != 2)
|
||||||
return error("Cannot do a twoway merge of %d trees\n",
|
return error("Cannot do a twoway merge of %d trees",
|
||||||
merge_size);
|
merge_size);
|
||||||
|
|
||||||
if (current) {
|
if (current) {
|
||||||
|
@ -616,7 +671,7 @@ static int oneway_merge(struct cache_entry **src)
|
||||||
struct cache_entry *a = src[1];
|
struct cache_entry *a = src[1];
|
||||||
|
|
||||||
if (merge_size != 1)
|
if (merge_size != 1)
|
||||||
return error("Cannot do a oneway merge of %d trees\n",
|
return error("Cannot do a oneway merge of %d trees",
|
||||||
merge_size);
|
merge_size);
|
||||||
|
|
||||||
if (!a)
|
if (!a)
|
||||||
|
@ -680,6 +735,11 @@ int main(int argc, char **argv)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strcmp(arg, "-v")) {
|
||||||
|
verbose_update = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* "-i" means "index only", meaning that a merge will
|
/* "-i" means "index only", meaning that a merge will
|
||||||
* not even look at the working tree.
|
* not even look at the working tree.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -92,7 +92,7 @@ static int run_update_hook(const char *refname,
|
||||||
case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
|
case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
|
||||||
return error("waitpid is confused");
|
return error("waitpid is confused");
|
||||||
case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
|
case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
|
||||||
return error("%s died of signal\n", update_hook);
|
return error("%s died of signal", update_hook);
|
||||||
case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
|
case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
|
||||||
return error("%s died strangely", update_hook);
|
return error("%s died strangely", update_hook);
|
||||||
default:
|
default:
|
||||||
|
@ -158,7 +158,7 @@ static int update(struct command *cmd)
|
||||||
if (run_update_hook(name, old_hex, new_hex)) {
|
if (run_update_hook(name, old_hex, new_hex)) {
|
||||||
unlink(lock_name);
|
unlink(lock_name);
|
||||||
cmd->error_string = "hook declined";
|
cmd->error_string = "hook declined";
|
||||||
return error("hook declined to update %s\n", name);
|
return error("hook declined to update %s", name);
|
||||||
}
|
}
|
||||||
else if (rename(lock_name, name) < 0) {
|
else if (rename(lock_name, name) < 0) {
|
||||||
unlink(lock_name);
|
unlink(lock_name);
|
||||||
|
|
2
refs.c
2
refs.c
|
@ -268,7 +268,7 @@ static int write_ref_file(const char *filename,
|
||||||
char term = '\n';
|
char term = '\n';
|
||||||
if (write(fd, hex, 40) < 40 ||
|
if (write(fd, hex, 40) < 40 ||
|
||||||
write(fd, &term, 1) < 1) {
|
write(fd, &term, 1) < 1) {
|
||||||
error("Couldn't write %s\n", filename);
|
error("Couldn't write %s", filename);
|
||||||
close(fd);
|
close(fd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
757
rev-list.c
757
rev-list.c
|
@ -4,14 +4,12 @@
|
||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
#include "epoch.h"
|
|
||||||
#include "diff.h"
|
#include "diff.h"
|
||||||
|
#include "revision.h"
|
||||||
|
|
||||||
#define SEEN (1u << 0)
|
/* bits #0-3 in revision.h */
|
||||||
#define INTERESTING (1u << 1)
|
|
||||||
#define COUNTED (1u << 2)
|
#define COUNTED (1u << 4)
|
||||||
#define SHOWN (1u << 3)
|
|
||||||
#define TREECHANGE (1u << 4)
|
|
||||||
#define TMP_MARK (1u << 5) /* for isolated cases; clean after use */
|
#define TMP_MARK (1u << 5) /* for isolated cases; clean after use */
|
||||||
|
|
||||||
static const char rev_list_usage[] =
|
static const char rev_list_usage[] =
|
||||||
|
@ -25,12 +23,11 @@ static const char rev_list_usage[] =
|
||||||
" --remove-empty\n"
|
" --remove-empty\n"
|
||||||
" --all\n"
|
" --all\n"
|
||||||
" ordering output:\n"
|
" ordering output:\n"
|
||||||
" --merge-order [ --show-breaks ]\n"
|
|
||||||
" --topo-order\n"
|
" --topo-order\n"
|
||||||
" --date-order\n"
|
" --date-order\n"
|
||||||
" formatting output:\n"
|
" formatting output:\n"
|
||||||
" --parents\n"
|
" --parents\n"
|
||||||
" --objects\n"
|
" --objects | --objects-edge\n"
|
||||||
" --unpacked\n"
|
" --unpacked\n"
|
||||||
" --header | --pretty\n"
|
" --header | --pretty\n"
|
||||||
" --abbrev=nr | --no-abbrev\n"
|
" --abbrev=nr | --no-abbrev\n"
|
||||||
|
@ -38,41 +35,18 @@ static const char rev_list_usage[] =
|
||||||
" --bisect"
|
" --bisect"
|
||||||
;
|
;
|
||||||
|
|
||||||
static int dense = 1;
|
struct rev_info revs;
|
||||||
static int unpacked = 0;
|
|
||||||
static int bisect_list = 0;
|
static int bisect_list = 0;
|
||||||
static int tag_objects = 0;
|
|
||||||
static int tree_objects = 0;
|
|
||||||
static int blob_objects = 0;
|
|
||||||
static int verbose_header = 0;
|
static int verbose_header = 0;
|
||||||
static int abbrev = DEFAULT_ABBREV;
|
static int abbrev = DEFAULT_ABBREV;
|
||||||
static int show_parents = 0;
|
static int show_parents = 0;
|
||||||
static int hdr_termination = 0;
|
static int hdr_termination = 0;
|
||||||
static const char *commit_prefix = "";
|
static const char *commit_prefix = "";
|
||||||
static unsigned long max_age = -1;
|
|
||||||
static unsigned long min_age = -1;
|
|
||||||
static int max_count = -1;
|
|
||||||
static enum cmit_fmt commit_format = CMIT_FMT_RAW;
|
static enum cmit_fmt commit_format = CMIT_FMT_RAW;
|
||||||
static int merge_order = 0;
|
|
||||||
static int show_breaks = 0;
|
|
||||||
static int stop_traversal = 0;
|
|
||||||
static int topo_order = 0;
|
|
||||||
static int lifo = 1;
|
|
||||||
static int no_merges = 0;
|
|
||||||
static const char **paths = NULL;
|
|
||||||
static int remove_empty_trees = 0;
|
|
||||||
|
|
||||||
static void show_commit(struct commit *commit)
|
static void show_commit(struct commit *commit)
|
||||||
{
|
{
|
||||||
commit->object.flags |= SHOWN;
|
|
||||||
if (show_breaks) {
|
|
||||||
commit_prefix = "| ";
|
|
||||||
if (commit->object.flags & DISCONTINUITY) {
|
|
||||||
commit_prefix = "^ ";
|
|
||||||
} else if (commit->object.flags & BOUNDARY) {
|
|
||||||
commit_prefix = "= ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("%s%s", commit_prefix, sha1_to_hex(commit->object.sha1));
|
printf("%s%s", commit_prefix, sha1_to_hex(commit->object.sha1));
|
||||||
if (show_parents) {
|
if (show_parents) {
|
||||||
struct commit_list *parents = commit->parents;
|
struct commit_list *parents = commit->parents;
|
||||||
|
@ -106,157 +80,87 @@ static void show_commit(struct commit *commit)
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rewrite_one(struct commit **pp)
|
static struct object_list **process_blob(struct blob *blob,
|
||||||
{
|
struct object_list **p,
|
||||||
for (;;) {
|
struct name_path *path,
|
||||||
struct commit *p = *pp;
|
const char *name)
|
||||||
if (p->object.flags & (TREECHANGE | UNINTERESTING))
|
|
||||||
return 0;
|
|
||||||
if (!p->parents)
|
|
||||||
return -1;
|
|
||||||
*pp = p->parents->item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rewrite_parents(struct commit *commit)
|
|
||||||
{
|
|
||||||
struct commit_list **pp = &commit->parents;
|
|
||||||
while (*pp) {
|
|
||||||
struct commit_list *parent = *pp;
|
|
||||||
if (rewrite_one(&parent->item) < 0) {
|
|
||||||
*pp = parent->next;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
pp = &parent->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int filter_commit(struct commit * commit)
|
|
||||||
{
|
|
||||||
if (stop_traversal && (commit->object.flags & BOUNDARY))
|
|
||||||
return STOP;
|
|
||||||
if (commit->object.flags & (UNINTERESTING|SHOWN))
|
|
||||||
return CONTINUE;
|
|
||||||
if (min_age != -1 && (commit->date > min_age))
|
|
||||||
return CONTINUE;
|
|
||||||
if (max_age != -1 && (commit->date < max_age)) {
|
|
||||||
stop_traversal=1;
|
|
||||||
return CONTINUE;
|
|
||||||
}
|
|
||||||
if (no_merges && (commit->parents && commit->parents->next))
|
|
||||||
return CONTINUE;
|
|
||||||
if (paths && dense) {
|
|
||||||
if (!(commit->object.flags & TREECHANGE))
|
|
||||||
return CONTINUE;
|
|
||||||
rewrite_parents(commit);
|
|
||||||
}
|
|
||||||
return DO;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_commit(struct commit * commit)
|
|
||||||
{
|
|
||||||
int action=filter_commit(commit);
|
|
||||||
|
|
||||||
if (action == STOP) {
|
|
||||||
return STOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action == CONTINUE) {
|
|
||||||
return CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (max_count != -1 && !max_count--)
|
|
||||||
return STOP;
|
|
||||||
|
|
||||||
show_commit(commit);
|
|
||||||
|
|
||||||
return CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct object_list **add_object(struct object *obj, struct object_list **p, const char *name)
|
|
||||||
{
|
|
||||||
struct object_list *entry = xmalloc(sizeof(*entry));
|
|
||||||
entry->item = obj;
|
|
||||||
entry->next = *p;
|
|
||||||
entry->name = name;
|
|
||||||
*p = entry;
|
|
||||||
return &entry->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct object_list **process_blob(struct blob *blob, struct object_list **p, const char *name)
|
|
||||||
{
|
{
|
||||||
struct object *obj = &blob->object;
|
struct object *obj = &blob->object;
|
||||||
|
|
||||||
if (!blob_objects)
|
if (!revs.blob_objects)
|
||||||
return p;
|
return p;
|
||||||
if (obj->flags & (UNINTERESTING | SEEN))
|
if (obj->flags & (UNINTERESTING | SEEN))
|
||||||
return p;
|
return p;
|
||||||
obj->flags |= SEEN;
|
obj->flags |= SEEN;
|
||||||
return add_object(obj, p, name);
|
return add_object(obj, p, path, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct object_list **process_tree(struct tree *tree, struct object_list **p, const char *name)
|
static struct object_list **process_tree(struct tree *tree,
|
||||||
|
struct object_list **p,
|
||||||
|
struct name_path *path,
|
||||||
|
const char *name)
|
||||||
{
|
{
|
||||||
struct object *obj = &tree->object;
|
struct object *obj = &tree->object;
|
||||||
struct tree_entry_list *entry;
|
struct tree_entry_list *entry;
|
||||||
|
struct name_path me;
|
||||||
|
|
||||||
if (!tree_objects)
|
if (!revs.tree_objects)
|
||||||
return p;
|
return p;
|
||||||
if (obj->flags & (UNINTERESTING | SEEN))
|
if (obj->flags & (UNINTERESTING | SEEN))
|
||||||
return p;
|
return p;
|
||||||
if (parse_tree(tree) < 0)
|
if (parse_tree(tree) < 0)
|
||||||
die("bad tree object %s", sha1_to_hex(obj->sha1));
|
die("bad tree object %s", sha1_to_hex(obj->sha1));
|
||||||
obj->flags |= SEEN;
|
obj->flags |= SEEN;
|
||||||
p = add_object(obj, p, name);
|
p = add_object(obj, p, path, name);
|
||||||
|
me.up = path;
|
||||||
|
me.elem = name;
|
||||||
|
me.elem_len = strlen(name);
|
||||||
entry = tree->entries;
|
entry = tree->entries;
|
||||||
tree->entries = NULL;
|
tree->entries = NULL;
|
||||||
while (entry) {
|
while (entry) {
|
||||||
struct tree_entry_list *next = entry->next;
|
struct tree_entry_list *next = entry->next;
|
||||||
if (entry->directory)
|
if (entry->directory)
|
||||||
p = process_tree(entry->item.tree, p, entry->name);
|
p = process_tree(entry->item.tree, p, &me, entry->name);
|
||||||
else
|
else
|
||||||
p = process_blob(entry->item.blob, p, entry->name);
|
p = process_blob(entry->item.blob, p, &me, entry->name);
|
||||||
free(entry);
|
free(entry);
|
||||||
entry = next;
|
entry = next;
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct object_list *pending_objects = NULL;
|
static void show_commit_list(struct rev_info *revs)
|
||||||
|
|
||||||
static void show_commit_list(struct commit_list *list)
|
|
||||||
{
|
{
|
||||||
|
struct commit *commit;
|
||||||
struct object_list *objects = NULL, **p = &objects, *pending;
|
struct object_list *objects = NULL, **p = &objects, *pending;
|
||||||
while (list) {
|
|
||||||
struct commit *commit = pop_most_recent_commit(&list, SEEN);
|
|
||||||
|
|
||||||
p = process_tree(commit->tree, p, "");
|
while ((commit = get_revision(revs)) != NULL) {
|
||||||
if (process_commit(commit) == STOP)
|
p = process_tree(commit->tree, p, NULL, "");
|
||||||
break;
|
show_commit(commit);
|
||||||
}
|
}
|
||||||
for (pending = pending_objects; pending; pending = pending->next) {
|
for (pending = revs->pending_objects; pending; pending = pending->next) {
|
||||||
struct object *obj = pending->item;
|
struct object *obj = pending->item;
|
||||||
const char *name = pending->name;
|
const char *name = pending->name;
|
||||||
if (obj->flags & (UNINTERESTING | SEEN))
|
if (obj->flags & (UNINTERESTING | SEEN))
|
||||||
continue;
|
continue;
|
||||||
if (obj->type == tag_type) {
|
if (obj->type == tag_type) {
|
||||||
obj->flags |= SEEN;
|
obj->flags |= SEEN;
|
||||||
p = add_object(obj, p, name);
|
p = add_object(obj, p, NULL, name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (obj->type == tree_type) {
|
if (obj->type == tree_type) {
|
||||||
p = process_tree((struct tree *)obj, p, name);
|
p = process_tree((struct tree *)obj, p, NULL, name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (obj->type == blob_type) {
|
if (obj->type == blob_type) {
|
||||||
p = process_blob((struct blob *)obj, p, name);
|
p = process_blob((struct blob *)obj, p, NULL, name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
|
die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
|
||||||
}
|
}
|
||||||
while (objects) {
|
while (objects) {
|
||||||
/* An object with name "foo\n0000000000000000000000000000000000000000"
|
/* An object with name "foo\n0000000..." can be used to
|
||||||
* can be used confuse downstream git-pack-objects very badly.
|
* confuse downstream git-pack-objects very badly.
|
||||||
*/
|
*/
|
||||||
const char *ep = strchr(objects->name, '\n');
|
const char *ep = strchr(objects->name, '\n');
|
||||||
if (ep) {
|
if (ep) {
|
||||||
|
@ -270,88 +174,6 @@ static void show_commit_list(struct commit_list *list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mark_blob_uninteresting(struct blob *blob)
|
|
||||||
{
|
|
||||||
if (!blob_objects)
|
|
||||||
return;
|
|
||||||
if (blob->object.flags & UNINTERESTING)
|
|
||||||
return;
|
|
||||||
blob->object.flags |= UNINTERESTING;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mark_tree_uninteresting(struct tree *tree)
|
|
||||||
{
|
|
||||||
struct object *obj = &tree->object;
|
|
||||||
struct tree_entry_list *entry;
|
|
||||||
|
|
||||||
if (!tree_objects)
|
|
||||||
return;
|
|
||||||
if (obj->flags & UNINTERESTING)
|
|
||||||
return;
|
|
||||||
obj->flags |= UNINTERESTING;
|
|
||||||
if (!has_sha1_file(obj->sha1))
|
|
||||||
return;
|
|
||||||
if (parse_tree(tree) < 0)
|
|
||||||
die("bad tree %s", sha1_to_hex(obj->sha1));
|
|
||||||
entry = tree->entries;
|
|
||||||
tree->entries = NULL;
|
|
||||||
while (entry) {
|
|
||||||
struct tree_entry_list *next = entry->next;
|
|
||||||
if (entry->directory)
|
|
||||||
mark_tree_uninteresting(entry->item.tree);
|
|
||||||
else
|
|
||||||
mark_blob_uninteresting(entry->item.blob);
|
|
||||||
free(entry);
|
|
||||||
entry = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mark_parents_uninteresting(struct commit *commit)
|
|
||||||
{
|
|
||||||
struct commit_list *parents = commit->parents;
|
|
||||||
|
|
||||||
while (parents) {
|
|
||||||
struct commit *commit = parents->item;
|
|
||||||
commit->object.flags |= UNINTERESTING;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Normally we haven't parsed the parent
|
|
||||||
* yet, so we won't have a parent of a parent
|
|
||||||
* here. However, it may turn out that we've
|
|
||||||
* reached this commit some other way (where it
|
|
||||||
* wasn't uninteresting), in which case we need
|
|
||||||
* to mark its parents recursively too..
|
|
||||||
*/
|
|
||||||
if (commit->parents)
|
|
||||||
mark_parents_uninteresting(commit);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A missing commit is ok iff its parent is marked
|
|
||||||
* uninteresting.
|
|
||||||
*
|
|
||||||
* We just mark such a thing parsed, so that when
|
|
||||||
* it is popped next time around, we won't be trying
|
|
||||||
* to parse it and get an error.
|
|
||||||
*/
|
|
||||||
if (!has_sha1_file(commit->object.sha1))
|
|
||||||
commit->object.parsed = 1;
|
|
||||||
parents = parents->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int everybody_uninteresting(struct commit_list *orig)
|
|
||||||
{
|
|
||||||
struct commit_list *list = orig;
|
|
||||||
while (list) {
|
|
||||||
struct commit *commit = list->item;
|
|
||||||
list = list->next;
|
|
||||||
if (commit->object.flags & UNINTERESTING)
|
|
||||||
continue;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is a truly stupid algorithm, but it's only
|
* This is a truly stupid algorithm, but it's only
|
||||||
* used for bisection, and we just don't care enough.
|
* used for bisection, and we just don't care enough.
|
||||||
|
@ -369,7 +191,7 @@ static int count_distance(struct commit_list *entry)
|
||||||
|
|
||||||
if (commit->object.flags & (UNINTERESTING | COUNTED))
|
if (commit->object.flags & (UNINTERESTING | COUNTED))
|
||||||
break;
|
break;
|
||||||
if (!paths || (commit->object.flags & TREECHANGE))
|
if (!revs.paths || (commit->object.flags & TREECHANGE))
|
||||||
nr++;
|
nr++;
|
||||||
commit->object.flags |= COUNTED;
|
commit->object.flags |= COUNTED;
|
||||||
p = commit->parents;
|
p = commit->parents;
|
||||||
|
@ -403,7 +225,7 @@ static struct commit_list *find_bisection(struct commit_list *list)
|
||||||
nr = 0;
|
nr = 0;
|
||||||
p = list;
|
p = list;
|
||||||
while (p) {
|
while (p) {
|
||||||
if (!paths || (p->item->object.flags & TREECHANGE))
|
if (!revs.paths || (p->item->object.flags & TREECHANGE))
|
||||||
nr++;
|
nr++;
|
||||||
p = p->next;
|
p = p->next;
|
||||||
}
|
}
|
||||||
|
@ -413,7 +235,7 @@ static struct commit_list *find_bisection(struct commit_list *list)
|
||||||
for (p = list; p; p = p->next) {
|
for (p = list; p; p = p->next) {
|
||||||
int distance;
|
int distance;
|
||||||
|
|
||||||
if (paths && !(p->item->object.flags & TREECHANGE))
|
if (revs.paths && !(p->item->object.flags & TREECHANGE))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
distance = count_distance(p);
|
distance = count_distance(p);
|
||||||
|
@ -430,369 +252,58 @@ static struct commit_list *find_bisection(struct commit_list *list)
|
||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mark_edge_parents_uninteresting(struct commit *commit)
|
||||||
|
{
|
||||||
|
struct commit_list *parents;
|
||||||
|
|
||||||
|
for (parents = commit->parents; parents; parents = parents->next) {
|
||||||
|
struct commit *parent = parents->item;
|
||||||
|
if (!(parent->object.flags & UNINTERESTING))
|
||||||
|
continue;
|
||||||
|
mark_tree_uninteresting(parent->tree);
|
||||||
|
if (revs.edge_hint && !(parent->object.flags & SHOWN)) {
|
||||||
|
parent->object.flags |= SHOWN;
|
||||||
|
printf("-%s\n", sha1_to_hex(parent->object.sha1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void mark_edges_uninteresting(struct commit_list *list)
|
static void mark_edges_uninteresting(struct commit_list *list)
|
||||||
{
|
{
|
||||||
for ( ; list; list = list->next) {
|
for ( ; list; list = list->next) {
|
||||||
struct commit_list *parents = list->item->parents;
|
|
||||||
|
|
||||||
for ( ; parents; parents = parents->next) {
|
|
||||||
struct commit *commit = parents->item;
|
|
||||||
if (commit->object.flags & UNINTERESTING)
|
|
||||||
mark_tree_uninteresting(commit->tree);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define TREE_SAME 0
|
|
||||||
#define TREE_NEW 1
|
|
||||||
#define TREE_DIFFERENT 2
|
|
||||||
static int tree_difference = TREE_SAME;
|
|
||||||
|
|
||||||
static void file_add_remove(struct diff_options *options,
|
|
||||||
int addremove, unsigned mode,
|
|
||||||
const unsigned char *sha1,
|
|
||||||
const char *base, const char *path)
|
|
||||||
{
|
|
||||||
int diff = TREE_DIFFERENT;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Is it an add of a new file? It means that
|
|
||||||
* the old tree didn't have it at all, so we
|
|
||||||
* will turn "TREE_SAME" -> "TREE_NEW", but
|
|
||||||
* leave any "TREE_DIFFERENT" alone (and if
|
|
||||||
* it already was "TREE_NEW", we'll keep it
|
|
||||||
* "TREE_NEW" of course).
|
|
||||||
*/
|
|
||||||
if (addremove == '+') {
|
|
||||||
diff = tree_difference;
|
|
||||||
if (diff != TREE_SAME)
|
|
||||||
return;
|
|
||||||
diff = TREE_NEW;
|
|
||||||
}
|
|
||||||
tree_difference = diff;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void file_change(struct diff_options *options,
|
|
||||||
unsigned old_mode, unsigned new_mode,
|
|
||||||
const unsigned char *old_sha1,
|
|
||||||
const unsigned char *new_sha1,
|
|
||||||
const char *base, const char *path)
|
|
||||||
{
|
|
||||||
tree_difference = TREE_DIFFERENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct diff_options diff_opt = {
|
|
||||||
.recursive = 1,
|
|
||||||
.add_remove = file_add_remove,
|
|
||||||
.change = file_change,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int compare_tree(struct tree *t1, struct tree *t2)
|
|
||||||
{
|
|
||||||
if (!t1)
|
|
||||||
return TREE_NEW;
|
|
||||||
if (!t2)
|
|
||||||
return TREE_DIFFERENT;
|
|
||||||
tree_difference = TREE_SAME;
|
|
||||||
if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "", &diff_opt) < 0)
|
|
||||||
return TREE_DIFFERENT;
|
|
||||||
return tree_difference;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int same_tree_as_empty(struct tree *t1)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
void *tree;
|
|
||||||
struct tree_desc empty, real;
|
|
||||||
|
|
||||||
if (!t1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
tree = read_object_with_reference(t1->object.sha1, "tree", &real.size, NULL);
|
|
||||||
if (!tree)
|
|
||||||
return 0;
|
|
||||||
real.buf = tree;
|
|
||||||
|
|
||||||
empty.buf = "";
|
|
||||||
empty.size = 0;
|
|
||||||
|
|
||||||
tree_difference = 0;
|
|
||||||
retval = diff_tree(&empty, &real, "", &diff_opt);
|
|
||||||
free(tree);
|
|
||||||
|
|
||||||
return retval >= 0 && !tree_difference;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void try_to_simplify_commit(struct commit *commit)
|
|
||||||
{
|
|
||||||
struct commit_list **pp, *parent;
|
|
||||||
|
|
||||||
if (!commit->tree)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!commit->parents) {
|
|
||||||
if (!same_tree_as_empty(commit->tree))
|
|
||||||
commit->object.flags |= TREECHANGE;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pp = &commit->parents;
|
|
||||||
while ((parent = *pp) != NULL) {
|
|
||||||
struct commit *p = parent->item;
|
|
||||||
|
|
||||||
if (p->object.flags & UNINTERESTING) {
|
|
||||||
pp = &parent->next;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
parse_commit(p);
|
|
||||||
switch (compare_tree(p->tree, commit->tree)) {
|
|
||||||
case TREE_SAME:
|
|
||||||
parent->next = NULL;
|
|
||||||
commit->parents = parent;
|
|
||||||
return;
|
|
||||||
|
|
||||||
case TREE_NEW:
|
|
||||||
if (remove_empty_trees && same_tree_as_empty(p->tree)) {
|
|
||||||
*pp = parent->next;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* fallthrough */
|
|
||||||
case TREE_DIFFERENT:
|
|
||||||
pp = &parent->next;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1));
|
|
||||||
}
|
|
||||||
commit->object.flags |= TREECHANGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add_parents_to_list(struct commit *commit, struct commit_list **list)
|
|
||||||
{
|
|
||||||
struct commit_list *parent = commit->parents;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the commit is uninteresting, don't try to
|
|
||||||
* prune parents - we want the maximal uninteresting
|
|
||||||
* set.
|
|
||||||
*
|
|
||||||
* Normally we haven't parsed the parent
|
|
||||||
* yet, so we won't have a parent of a parent
|
|
||||||
* here. However, it may turn out that we've
|
|
||||||
* reached this commit some other way (where it
|
|
||||||
* wasn't uninteresting), in which case we need
|
|
||||||
* to mark its parents recursively too..
|
|
||||||
*/
|
|
||||||
if (commit->object.flags & UNINTERESTING) {
|
|
||||||
while (parent) {
|
|
||||||
struct commit *p = parent->item;
|
|
||||||
parent = parent->next;
|
|
||||||
parse_commit(p);
|
|
||||||
p->object.flags |= UNINTERESTING;
|
|
||||||
if (p->parents)
|
|
||||||
mark_parents_uninteresting(p);
|
|
||||||
if (p->object.flags & SEEN)
|
|
||||||
continue;
|
|
||||||
p->object.flags |= SEEN;
|
|
||||||
insert_by_date(p, list);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ok, the commit wasn't uninteresting. Try to
|
|
||||||
* simplify the commit history and find the parent
|
|
||||||
* that has no differences in the path set if one exists.
|
|
||||||
*/
|
|
||||||
if (paths)
|
|
||||||
try_to_simplify_commit(commit);
|
|
||||||
|
|
||||||
parent = commit->parents;
|
|
||||||
while (parent) {
|
|
||||||
struct commit *p = parent->item;
|
|
||||||
|
|
||||||
parent = parent->next;
|
|
||||||
|
|
||||||
parse_commit(p);
|
|
||||||
if (p->object.flags & SEEN)
|
|
||||||
continue;
|
|
||||||
p->object.flags |= SEEN;
|
|
||||||
insert_by_date(p, list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct commit_list *limit_list(struct commit_list *list)
|
|
||||||
{
|
|
||||||
struct commit_list *newlist = NULL;
|
|
||||||
struct commit_list **p = &newlist;
|
|
||||||
while (list) {
|
|
||||||
struct commit_list *entry = list;
|
|
||||||
struct commit *commit = list->item;
|
struct commit *commit = list->item;
|
||||||
struct object *obj = &commit->object;
|
|
||||||
|
|
||||||
list = list->next;
|
if (commit->object.flags & UNINTERESTING) {
|
||||||
free(entry);
|
mark_tree_uninteresting(commit->tree);
|
||||||
|
|
||||||
if (max_age != -1 && (commit->date < max_age))
|
|
||||||
obj->flags |= UNINTERESTING;
|
|
||||||
if (unpacked && has_sha1_pack(obj->sha1))
|
|
||||||
obj->flags |= UNINTERESTING;
|
|
||||||
add_parents_to_list(commit, &list);
|
|
||||||
if (obj->flags & UNINTERESTING) {
|
|
||||||
mark_parents_uninteresting(commit);
|
|
||||||
if (everybody_uninteresting(list))
|
|
||||||
break;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (min_age != -1 && (commit->date > min_age))
|
mark_edge_parents_uninteresting(commit);
|
||||||
continue;
|
|
||||||
p = &commit_list_insert(commit, p)->next;
|
|
||||||
}
|
}
|
||||||
if (tree_objects)
|
|
||||||
mark_edges_uninteresting(newlist);
|
|
||||||
if (bisect_list)
|
|
||||||
newlist = find_bisection(newlist);
|
|
||||||
return newlist;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add_pending_object(struct object *obj, const char *name)
|
|
||||||
{
|
|
||||||
add_object(obj, &pending_objects, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct commit *get_commit_reference(const char *name, const unsigned char *sha1, unsigned int flags)
|
|
||||||
{
|
|
||||||
struct object *object;
|
|
||||||
|
|
||||||
object = parse_object(sha1);
|
|
||||||
if (!object)
|
|
||||||
die("bad object %s", name);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tag object? Look what it points to..
|
|
||||||
*/
|
|
||||||
while (object->type == tag_type) {
|
|
||||||
struct tag *tag = (struct tag *) object;
|
|
||||||
object->flags |= flags;
|
|
||||||
if (tag_objects && !(object->flags & UNINTERESTING))
|
|
||||||
add_pending_object(object, tag->tag);
|
|
||||||
object = parse_object(tag->tagged->sha1);
|
|
||||||
if (!object)
|
|
||||||
die("bad object %s", sha1_to_hex(tag->tagged->sha1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Commit object? Just return it, we'll do all the complex
|
|
||||||
* reachability crud.
|
|
||||||
*/
|
|
||||||
if (object->type == commit_type) {
|
|
||||||
struct commit *commit = (struct commit *)object;
|
|
||||||
object->flags |= flags;
|
|
||||||
if (parse_commit(commit) < 0)
|
|
||||||
die("unable to parse commit %s", name);
|
|
||||||
if (flags & UNINTERESTING)
|
|
||||||
mark_parents_uninteresting(commit);
|
|
||||||
return commit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tree object? Either mark it uniniteresting, or add it
|
|
||||||
* to the list of objects to look at later..
|
|
||||||
*/
|
|
||||||
if (object->type == tree_type) {
|
|
||||||
struct tree *tree = (struct tree *)object;
|
|
||||||
if (!tree_objects)
|
|
||||||
return NULL;
|
|
||||||
if (flags & UNINTERESTING) {
|
|
||||||
mark_tree_uninteresting(tree);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
add_pending_object(object, "");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Blob object? You know the drill by now..
|
|
||||||
*/
|
|
||||||
if (object->type == blob_type) {
|
|
||||||
struct blob *blob = (struct blob *)object;
|
|
||||||
if (!blob_objects)
|
|
||||||
return NULL;
|
|
||||||
if (flags & UNINTERESTING) {
|
|
||||||
mark_blob_uninteresting(blob);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
add_pending_object(object, "");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
die("%s is unknown object", name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_one_commit(struct commit *com, struct commit_list **lst)
|
|
||||||
{
|
|
||||||
if (!com || com->object.flags & SEEN)
|
|
||||||
return;
|
|
||||||
com->object.flags |= SEEN;
|
|
||||||
commit_list_insert(com, lst);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* for_each_ref() callback does not allow user data -- Yuck. */
|
|
||||||
static struct commit_list **global_lst;
|
|
||||||
|
|
||||||
static int include_one_commit(const char *path, const unsigned char *sha1)
|
|
||||||
{
|
|
||||||
struct commit *com = get_commit_reference(path, sha1, 0);
|
|
||||||
handle_one_commit(com, global_lst);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_all(struct commit_list **lst)
|
|
||||||
{
|
|
||||||
global_lst = lst;
|
|
||||||
for_each_ref(include_one_commit);
|
|
||||||
global_lst = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char **argv)
|
int main(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
const char *prefix = setup_git_directory();
|
struct commit_list *list;
|
||||||
struct commit_list *list = NULL;
|
int i;
|
||||||
int i, limited = 0;
|
|
||||||
|
argc = setup_revisions(argc, argv, &revs, NULL);
|
||||||
|
|
||||||
for (i = 1 ; i < argc; i++) {
|
for (i = 1 ; i < argc; i++) {
|
||||||
int flags;
|
|
||||||
const char *arg = argv[i];
|
const char *arg = argv[i];
|
||||||
char *dotdot;
|
|
||||||
struct commit *commit;
|
|
||||||
unsigned char sha1[20];
|
|
||||||
|
|
||||||
/* accept -<digit>, like traditilnal "head" */
|
/* accept -<digit>, like traditilnal "head" */
|
||||||
if ((*arg == '-') && isdigit(arg[1])) {
|
if ((*arg == '-') && isdigit(arg[1])) {
|
||||||
max_count = atoi(arg + 1);
|
revs.max_count = atoi(arg + 1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(arg, "-n")) {
|
if (!strcmp(arg, "-n")) {
|
||||||
if (++i >= argc)
|
if (++i >= argc)
|
||||||
die("-n requires an argument");
|
die("-n requires an argument");
|
||||||
max_count = atoi(argv[i]);
|
revs.max_count = atoi(argv[i]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strncmp(arg,"-n",2)) {
|
if (!strncmp(arg,"-n",2)) {
|
||||||
max_count = atoi(arg + 2);
|
revs.max_count = atoi(arg + 2);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strncmp(arg, "--max-count=", 12)) {
|
|
||||||
max_count = atoi(arg + 12);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strncmp(arg, "--max-age=", 10)) {
|
|
||||||
max_age = atoi(arg + 10);
|
|
||||||
limited = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strncmp(arg, "--min-age=", 10)) {
|
|
||||||
min_age = atoi(arg + 10);
|
|
||||||
limited = 1;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(arg, "--header")) {
|
if (!strcmp(arg, "--header")) {
|
||||||
|
@ -821,10 +332,6 @@ int main(int argc, const char **argv)
|
||||||
commit_prefix = "commit ";
|
commit_prefix = "commit ";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strncmp(arg, "--no-merges", 11)) {
|
|
||||||
no_merges = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strcmp(arg, "--parents")) {
|
if (!strcmp(arg, "--parents")) {
|
||||||
show_parents = 1;
|
show_parents = 1;
|
||||||
continue;
|
continue;
|
||||||
|
@ -833,133 +340,27 @@ int main(int argc, const char **argv)
|
||||||
bisect_list = 1;
|
bisect_list = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(arg, "--all")) {
|
|
||||||
handle_all(&list);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strcmp(arg, "--objects")) {
|
|
||||||
tag_objects = 1;
|
|
||||||
tree_objects = 1;
|
|
||||||
blob_objects = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strcmp(arg, "--unpacked")) {
|
|
||||||
unpacked = 1;
|
|
||||||
limited = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strcmp(arg, "--merge-order")) {
|
|
||||||
merge_order = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strcmp(arg, "--show-breaks")) {
|
|
||||||
show_breaks = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strcmp(arg, "--topo-order")) {
|
|
||||||
topo_order = 1;
|
|
||||||
lifo = 1;
|
|
||||||
limited = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strcmp(arg, "--date-order")) {
|
|
||||||
topo_order = 1;
|
|
||||||
lifo = 0;
|
|
||||||
limited = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strcmp(arg, "--dense")) {
|
|
||||||
dense = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strcmp(arg, "--sparse")) {
|
|
||||||
dense = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strcmp(arg, "--remove-empty")) {
|
|
||||||
remove_empty_trees = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strcmp(arg, "--")) {
|
|
||||||
i++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (show_breaks && !merge_order)
|
|
||||||
usage(rev_list_usage);
|
|
||||||
|
|
||||||
flags = 0;
|
|
||||||
dotdot = strstr(arg, "..");
|
|
||||||
if (dotdot) {
|
|
||||||
unsigned char from_sha1[20];
|
|
||||||
char *next = dotdot + 2;
|
|
||||||
*dotdot = 0;
|
|
||||||
if (!*next)
|
|
||||||
next = "HEAD";
|
|
||||||
if (!get_sha1(arg, from_sha1) && !get_sha1(next, sha1)) {
|
|
||||||
struct commit *exclude;
|
|
||||||
struct commit *include;
|
|
||||||
|
|
||||||
exclude = get_commit_reference(arg, from_sha1, UNINTERESTING);
|
|
||||||
include = get_commit_reference(next, sha1, 0);
|
|
||||||
if (!exclude || !include)
|
|
||||||
die("Invalid revision range %s..%s", arg, next);
|
|
||||||
limited = 1;
|
|
||||||
handle_one_commit(exclude, &list);
|
|
||||||
handle_one_commit(include, &list);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
*dotdot = '.';
|
|
||||||
}
|
|
||||||
if (*arg == '^') {
|
|
||||||
flags = UNINTERESTING;
|
|
||||||
arg++;
|
|
||||||
limited = 1;
|
|
||||||
}
|
|
||||||
if (get_sha1(arg, sha1) < 0) {
|
|
||||||
struct stat st;
|
|
||||||
if (lstat(arg, &st) < 0)
|
|
||||||
die("'%s': %s", arg, strerror(errno));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
commit = get_commit_reference(arg, sha1, flags);
|
|
||||||
handle_one_commit(commit, &list);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!list &&
|
|
||||||
(!(tag_objects||tree_objects||blob_objects) && !pending_objects))
|
|
||||||
usage(rev_list_usage);
|
usage(rev_list_usage);
|
||||||
|
|
||||||
paths = get_pathspec(prefix, argv + i);
|
|
||||||
if (paths) {
|
|
||||||
limited = 1;
|
|
||||||
diff_tree_setup_paths(paths);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list = revs.commits;
|
||||||
|
|
||||||
|
if (!list &&
|
||||||
|
(!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && !revs.pending_objects))
|
||||||
|
usage(rev_list_usage);
|
||||||
|
|
||||||
|
prepare_revision_walk(&revs);
|
||||||
|
if (revs.tree_objects)
|
||||||
|
mark_edges_uninteresting(revs.commits);
|
||||||
|
|
||||||
|
if (bisect_list)
|
||||||
|
revs.commits = find_bisection(revs.commits);
|
||||||
|
|
||||||
save_commit_buffer = verbose_header;
|
save_commit_buffer = verbose_header;
|
||||||
track_object_refs = 0;
|
track_object_refs = 0;
|
||||||
|
|
||||||
if (!merge_order) {
|
show_commit_list(&revs);
|
||||||
sort_by_date(&list);
|
|
||||||
if (list && !limited && max_count == 1 &&
|
|
||||||
!tag_objects && !tree_objects && !blob_objects) {
|
|
||||||
show_commit(list->item);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (limited)
|
|
||||||
list = limit_list(list);
|
|
||||||
if (topo_order)
|
|
||||||
sort_in_topological_order(&list, lifo);
|
|
||||||
show_commit_list(list);
|
|
||||||
} else {
|
|
||||||
#ifndef NO_OPENSSL
|
|
||||||
if (sort_list_in_merge_order(list, &process_commit)) {
|
|
||||||
die("merge order sort failed\n");
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
die("merge order sort unsupported, OpenSSL not linked");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,13 +39,12 @@ static int is_rev_argument(const char *arg)
|
||||||
"--header",
|
"--header",
|
||||||
"--max-age=",
|
"--max-age=",
|
||||||
"--max-count=",
|
"--max-count=",
|
||||||
"--merge-order",
|
|
||||||
"--min-age=",
|
"--min-age=",
|
||||||
"--no-merges",
|
"--no-merges",
|
||||||
"--objects",
|
"--objects",
|
||||||
|
"--objects-edge",
|
||||||
"--parents",
|
"--parents",
|
||||||
"--pretty",
|
"--pretty",
|
||||||
"--show-breaks",
|
|
||||||
"--sparse",
|
"--sparse",
|
||||||
"--topo-order",
|
"--topo-order",
|
||||||
"--date-order",
|
"--date-order",
|
||||||
|
|
|
@ -0,0 +1,687 @@
|
||||||
|
#include "cache.h"
|
||||||
|
#include "tag.h"
|
||||||
|
#include "blob.h"
|
||||||
|
#include "tree.h"
|
||||||
|
#include "commit.h"
|
||||||
|
#include "diff.h"
|
||||||
|
#include "refs.h"
|
||||||
|
#include "revision.h"
|
||||||
|
|
||||||
|
static char *path_name(struct name_path *path, const char *name)
|
||||||
|
{
|
||||||
|
struct name_path *p;
|
||||||
|
char *n, *m;
|
||||||
|
int nlen = strlen(name);
|
||||||
|
int len = nlen + 1;
|
||||||
|
|
||||||
|
for (p = path; p; p = p->up) {
|
||||||
|
if (p->elem_len)
|
||||||
|
len += p->elem_len + 1;
|
||||||
|
}
|
||||||
|
n = xmalloc(len);
|
||||||
|
m = n + len - (nlen + 1);
|
||||||
|
strcpy(m, name);
|
||||||
|
for (p = path; p; p = p->up) {
|
||||||
|
if (p->elem_len) {
|
||||||
|
m -= p->elem_len + 1;
|
||||||
|
memcpy(m, p->elem, p->elem_len);
|
||||||
|
m[p->elem_len] = '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct object_list **add_object(struct object *obj,
|
||||||
|
struct object_list **p,
|
||||||
|
struct name_path *path,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
struct object_list *entry = xmalloc(sizeof(*entry));
|
||||||
|
entry->item = obj;
|
||||||
|
entry->next = *p;
|
||||||
|
entry->name = path_name(path, name);
|
||||||
|
*p = entry;
|
||||||
|
return &entry->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mark_blob_uninteresting(struct blob *blob)
|
||||||
|
{
|
||||||
|
if (blob->object.flags & UNINTERESTING)
|
||||||
|
return;
|
||||||
|
blob->object.flags |= UNINTERESTING;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mark_tree_uninteresting(struct tree *tree)
|
||||||
|
{
|
||||||
|
struct object *obj = &tree->object;
|
||||||
|
struct tree_entry_list *entry;
|
||||||
|
|
||||||
|
if (obj->flags & UNINTERESTING)
|
||||||
|
return;
|
||||||
|
obj->flags |= UNINTERESTING;
|
||||||
|
if (!has_sha1_file(obj->sha1))
|
||||||
|
return;
|
||||||
|
if (parse_tree(tree) < 0)
|
||||||
|
die("bad tree %s", sha1_to_hex(obj->sha1));
|
||||||
|
entry = tree->entries;
|
||||||
|
tree->entries = NULL;
|
||||||
|
while (entry) {
|
||||||
|
struct tree_entry_list *next = entry->next;
|
||||||
|
if (entry->directory)
|
||||||
|
mark_tree_uninteresting(entry->item.tree);
|
||||||
|
else
|
||||||
|
mark_blob_uninteresting(entry->item.blob);
|
||||||
|
free(entry);
|
||||||
|
entry = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mark_parents_uninteresting(struct commit *commit)
|
||||||
|
{
|
||||||
|
struct commit_list *parents = commit->parents;
|
||||||
|
|
||||||
|
while (parents) {
|
||||||
|
struct commit *commit = parents->item;
|
||||||
|
commit->object.flags |= UNINTERESTING;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Normally we haven't parsed the parent
|
||||||
|
* yet, so we won't have a parent of a parent
|
||||||
|
* here. However, it may turn out that we've
|
||||||
|
* reached this commit some other way (where it
|
||||||
|
* wasn't uninteresting), in which case we need
|
||||||
|
* to mark its parents recursively too..
|
||||||
|
*/
|
||||||
|
if (commit->parents)
|
||||||
|
mark_parents_uninteresting(commit);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A missing commit is ok iff its parent is marked
|
||||||
|
* uninteresting.
|
||||||
|
*
|
||||||
|
* We just mark such a thing parsed, so that when
|
||||||
|
* it is popped next time around, we won't be trying
|
||||||
|
* to parse it and get an error.
|
||||||
|
*/
|
||||||
|
if (!has_sha1_file(commit->object.sha1))
|
||||||
|
commit->object.parsed = 1;
|
||||||
|
parents = parents->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
|
||||||
|
{
|
||||||
|
add_object(obj, &revs->pending_objects, NULL, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct commit *get_commit_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct object *object;
|
||||||
|
|
||||||
|
object = parse_object(sha1);
|
||||||
|
if (!object)
|
||||||
|
die("bad object %s", name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tag object? Look what it points to..
|
||||||
|
*/
|
||||||
|
while (object->type == tag_type) {
|
||||||
|
struct tag *tag = (struct tag *) object;
|
||||||
|
object->flags |= flags;
|
||||||
|
if (revs->tag_objects && !(object->flags & UNINTERESTING))
|
||||||
|
add_pending_object(revs, object, tag->tag);
|
||||||
|
object = parse_object(tag->tagged->sha1);
|
||||||
|
if (!object)
|
||||||
|
die("bad object %s", sha1_to_hex(tag->tagged->sha1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Commit object? Just return it, we'll do all the complex
|
||||||
|
* reachability crud.
|
||||||
|
*/
|
||||||
|
if (object->type == commit_type) {
|
||||||
|
struct commit *commit = (struct commit *)object;
|
||||||
|
object->flags |= flags;
|
||||||
|
if (parse_commit(commit) < 0)
|
||||||
|
die("unable to parse commit %s", name);
|
||||||
|
if (flags & UNINTERESTING) {
|
||||||
|
mark_parents_uninteresting(commit);
|
||||||
|
revs->limited = 1;
|
||||||
|
}
|
||||||
|
return commit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tree object? Either mark it uniniteresting, or add it
|
||||||
|
* to the list of objects to look at later..
|
||||||
|
*/
|
||||||
|
if (object->type == tree_type) {
|
||||||
|
struct tree *tree = (struct tree *)object;
|
||||||
|
if (!revs->tree_objects)
|
||||||
|
return NULL;
|
||||||
|
if (flags & UNINTERESTING) {
|
||||||
|
mark_tree_uninteresting(tree);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
add_pending_object(revs, object, "");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Blob object? You know the drill by now..
|
||||||
|
*/
|
||||||
|
if (object->type == blob_type) {
|
||||||
|
struct blob *blob = (struct blob *)object;
|
||||||
|
if (!revs->blob_objects)
|
||||||
|
return NULL;
|
||||||
|
if (flags & UNINTERESTING) {
|
||||||
|
mark_blob_uninteresting(blob);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
add_pending_object(revs, object, "");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
die("%s is unknown object", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int everybody_uninteresting(struct commit_list *orig)
|
||||||
|
{
|
||||||
|
struct commit_list *list = orig;
|
||||||
|
while (list) {
|
||||||
|
struct commit *commit = list->item;
|
||||||
|
list = list->next;
|
||||||
|
if (commit->object.flags & UNINTERESTING)
|
||||||
|
continue;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TREE_SAME 0
|
||||||
|
#define TREE_NEW 1
|
||||||
|
#define TREE_DIFFERENT 2
|
||||||
|
static int tree_difference = TREE_SAME;
|
||||||
|
|
||||||
|
static void file_add_remove(struct diff_options *options,
|
||||||
|
int addremove, unsigned mode,
|
||||||
|
const unsigned char *sha1,
|
||||||
|
const char *base, const char *path)
|
||||||
|
{
|
||||||
|
int diff = TREE_DIFFERENT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Is it an add of a new file? It means that
|
||||||
|
* the old tree didn't have it at all, so we
|
||||||
|
* will turn "TREE_SAME" -> "TREE_NEW", but
|
||||||
|
* leave any "TREE_DIFFERENT" alone (and if
|
||||||
|
* it already was "TREE_NEW", we'll keep it
|
||||||
|
* "TREE_NEW" of course).
|
||||||
|
*/
|
||||||
|
if (addremove == '+') {
|
||||||
|
diff = tree_difference;
|
||||||
|
if (diff != TREE_SAME)
|
||||||
|
return;
|
||||||
|
diff = TREE_NEW;
|
||||||
|
}
|
||||||
|
tree_difference = diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void file_change(struct diff_options *options,
|
||||||
|
unsigned old_mode, unsigned new_mode,
|
||||||
|
const unsigned char *old_sha1,
|
||||||
|
const unsigned char *new_sha1,
|
||||||
|
const char *base, const char *path)
|
||||||
|
{
|
||||||
|
tree_difference = TREE_DIFFERENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct diff_options diff_opt = {
|
||||||
|
.recursive = 1,
|
||||||
|
.add_remove = file_add_remove,
|
||||||
|
.change = file_change,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int compare_tree(struct tree *t1, struct tree *t2)
|
||||||
|
{
|
||||||
|
if (!t1)
|
||||||
|
return TREE_NEW;
|
||||||
|
if (!t2)
|
||||||
|
return TREE_DIFFERENT;
|
||||||
|
tree_difference = TREE_SAME;
|
||||||
|
if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "", &diff_opt) < 0)
|
||||||
|
return TREE_DIFFERENT;
|
||||||
|
return tree_difference;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int same_tree_as_empty(struct tree *t1)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
void *tree;
|
||||||
|
struct tree_desc empty, real;
|
||||||
|
|
||||||
|
if (!t1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tree = read_object_with_reference(t1->object.sha1, "tree", &real.size, NULL);
|
||||||
|
if (!tree)
|
||||||
|
return 0;
|
||||||
|
real.buf = tree;
|
||||||
|
|
||||||
|
empty.buf = "";
|
||||||
|
empty.size = 0;
|
||||||
|
|
||||||
|
tree_difference = 0;
|
||||||
|
retval = diff_tree(&empty, &real, "", &diff_opt);
|
||||||
|
free(tree);
|
||||||
|
|
||||||
|
return retval >= 0 && !tree_difference;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
|
||||||
|
{
|
||||||
|
struct commit_list **pp, *parent;
|
||||||
|
|
||||||
|
if (!commit->tree)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!commit->parents) {
|
||||||
|
if (!same_tree_as_empty(commit->tree))
|
||||||
|
commit->object.flags |= TREECHANGE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pp = &commit->parents;
|
||||||
|
while ((parent = *pp) != NULL) {
|
||||||
|
struct commit *p = parent->item;
|
||||||
|
|
||||||
|
if (p->object.flags & UNINTERESTING) {
|
||||||
|
pp = &parent->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_commit(p);
|
||||||
|
switch (compare_tree(p->tree, commit->tree)) {
|
||||||
|
case TREE_SAME:
|
||||||
|
parent->next = NULL;
|
||||||
|
commit->parents = parent;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case TREE_NEW:
|
||||||
|
if (revs->remove_empty_trees && same_tree_as_empty(p->tree)) {
|
||||||
|
*pp = parent->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* fallthrough */
|
||||||
|
case TREE_DIFFERENT:
|
||||||
|
pp = &parent->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1));
|
||||||
|
}
|
||||||
|
commit->object.flags |= TREECHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_parents_to_list(struct rev_info *revs, struct commit *commit, struct commit_list **list)
|
||||||
|
{
|
||||||
|
struct commit_list *parent = commit->parents;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the commit is uninteresting, don't try to
|
||||||
|
* prune parents - we want the maximal uninteresting
|
||||||
|
* set.
|
||||||
|
*
|
||||||
|
* Normally we haven't parsed the parent
|
||||||
|
* yet, so we won't have a parent of a parent
|
||||||
|
* here. However, it may turn out that we've
|
||||||
|
* reached this commit some other way (where it
|
||||||
|
* wasn't uninteresting), in which case we need
|
||||||
|
* to mark its parents recursively too..
|
||||||
|
*/
|
||||||
|
if (commit->object.flags & UNINTERESTING) {
|
||||||
|
while (parent) {
|
||||||
|
struct commit *p = parent->item;
|
||||||
|
parent = parent->next;
|
||||||
|
parse_commit(p);
|
||||||
|
p->object.flags |= UNINTERESTING;
|
||||||
|
if (p->parents)
|
||||||
|
mark_parents_uninteresting(p);
|
||||||
|
if (p->object.flags & SEEN)
|
||||||
|
continue;
|
||||||
|
p->object.flags |= SEEN;
|
||||||
|
insert_by_date(p, list);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ok, the commit wasn't uninteresting. Try to
|
||||||
|
* simplify the commit history and find the parent
|
||||||
|
* that has no differences in the path set if one exists.
|
||||||
|
*/
|
||||||
|
if (revs->paths)
|
||||||
|
try_to_simplify_commit(revs, commit);
|
||||||
|
|
||||||
|
parent = commit->parents;
|
||||||
|
while (parent) {
|
||||||
|
struct commit *p = parent->item;
|
||||||
|
|
||||||
|
parent = parent->next;
|
||||||
|
|
||||||
|
parse_commit(p);
|
||||||
|
if (p->object.flags & SEEN)
|
||||||
|
continue;
|
||||||
|
p->object.flags |= SEEN;
|
||||||
|
insert_by_date(p, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void limit_list(struct rev_info *revs)
|
||||||
|
{
|
||||||
|
struct commit_list *list = revs->commits;
|
||||||
|
struct commit_list *newlist = NULL;
|
||||||
|
struct commit_list **p = &newlist;
|
||||||
|
|
||||||
|
if (revs->paths)
|
||||||
|
diff_tree_setup_paths(revs->paths);
|
||||||
|
|
||||||
|
while (list) {
|
||||||
|
struct commit_list *entry = list;
|
||||||
|
struct commit *commit = list->item;
|
||||||
|
struct object *obj = &commit->object;
|
||||||
|
|
||||||
|
list = list->next;
|
||||||
|
free(entry);
|
||||||
|
|
||||||
|
if (revs->max_age != -1 && (commit->date < revs->max_age))
|
||||||
|
obj->flags |= UNINTERESTING;
|
||||||
|
if (revs->unpacked && has_sha1_pack(obj->sha1))
|
||||||
|
obj->flags |= UNINTERESTING;
|
||||||
|
add_parents_to_list(revs, commit, &list);
|
||||||
|
if (obj->flags & UNINTERESTING) {
|
||||||
|
mark_parents_uninteresting(commit);
|
||||||
|
if (everybody_uninteresting(list))
|
||||||
|
break;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (revs->min_age != -1 && (commit->date > revs->min_age))
|
||||||
|
continue;
|
||||||
|
p = &commit_list_insert(commit, p)->next;
|
||||||
|
}
|
||||||
|
revs->commits = newlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_one_commit(struct commit *commit, struct rev_info *revs)
|
||||||
|
{
|
||||||
|
if (!commit || (commit->object.flags & SEEN))
|
||||||
|
return;
|
||||||
|
commit->object.flags |= SEEN;
|
||||||
|
commit_list_insert(commit, &revs->commits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int all_flags;
|
||||||
|
static struct rev_info *all_revs;
|
||||||
|
|
||||||
|
static int handle_one_ref(const char *path, const unsigned char *sha1)
|
||||||
|
{
|
||||||
|
struct commit *commit = get_commit_reference(all_revs, path, sha1, all_flags);
|
||||||
|
add_one_commit(commit, all_revs);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_all(struct rev_info *revs, unsigned flags)
|
||||||
|
{
|
||||||
|
all_revs = revs;
|
||||||
|
all_flags = flags;
|
||||||
|
for_each_ref(handle_one_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse revision information, filling in the "rev_info" structure,
|
||||||
|
* and removing the used arguments from the argument list.
|
||||||
|
*
|
||||||
|
* Returns the number of arguments left that weren't recognized
|
||||||
|
* (which are also moved to the head of the argument list)
|
||||||
|
*/
|
||||||
|
int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def)
|
||||||
|
{
|
||||||
|
int i, flags, seen_dashdash;
|
||||||
|
const char **unrecognized = argv + 1;
|
||||||
|
int left = 1;
|
||||||
|
|
||||||
|
memset(revs, 0, sizeof(*revs));
|
||||||
|
revs->lifo = 1;
|
||||||
|
revs->dense = 1;
|
||||||
|
revs->prefix = setup_git_directory();
|
||||||
|
revs->max_age = -1;
|
||||||
|
revs->min_age = -1;
|
||||||
|
revs->max_count = -1;
|
||||||
|
|
||||||
|
/* First, search for "--" */
|
||||||
|
seen_dashdash = 0;
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
const char *arg = argv[i];
|
||||||
|
if (strcmp(arg, "--"))
|
||||||
|
continue;
|
||||||
|
argv[i] = NULL;
|
||||||
|
argc = i;
|
||||||
|
revs->paths = get_pathspec(revs->prefix, argv + i + 1);
|
||||||
|
seen_dashdash = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = 0;
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
struct commit *commit;
|
||||||
|
const char *arg = argv[i];
|
||||||
|
unsigned char sha1[20];
|
||||||
|
char *dotdot;
|
||||||
|
int local_flags;
|
||||||
|
|
||||||
|
if (*arg == '-') {
|
||||||
|
if (!strncmp(arg, "--max-count=", 12)) {
|
||||||
|
revs->max_count = atoi(arg + 12);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strncmp(arg, "--max-age=", 10)) {
|
||||||
|
revs->max_age = atoi(arg + 10);
|
||||||
|
revs->limited = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strncmp(arg, "--min-age=", 10)) {
|
||||||
|
revs->min_age = atoi(arg + 10);
|
||||||
|
revs->limited = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(arg, "--all")) {
|
||||||
|
handle_all(revs, flags);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(arg, "--not")) {
|
||||||
|
flags ^= UNINTERESTING;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(arg, "--default")) {
|
||||||
|
if (++i >= argc)
|
||||||
|
die("bad --default argument");
|
||||||
|
def = argv[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(arg, "--topo-order")) {
|
||||||
|
revs->topo_order = 1;
|
||||||
|
revs->limited = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(arg, "--date-order")) {
|
||||||
|
revs->lifo = 0;
|
||||||
|
revs->topo_order = 1;
|
||||||
|
revs->limited = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(arg, "--dense")) {
|
||||||
|
revs->dense = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(arg, "--sparse")) {
|
||||||
|
revs->dense = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(arg, "--remove-empty")) {
|
||||||
|
revs->remove_empty_trees = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strncmp(arg, "--no-merges", 11)) {
|
||||||
|
revs->no_merges = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(arg, "--objects")) {
|
||||||
|
revs->tag_objects = 1;
|
||||||
|
revs->tree_objects = 1;
|
||||||
|
revs->blob_objects = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(arg, "--objects-edge")) {
|
||||||
|
revs->tag_objects = 1;
|
||||||
|
revs->tree_objects = 1;
|
||||||
|
revs->blob_objects = 1;
|
||||||
|
revs->edge_hint = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(arg, "--unpacked")) {
|
||||||
|
revs->unpacked = 1;
|
||||||
|
revs->limited = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*unrecognized++ = arg;
|
||||||
|
left++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dotdot = strstr(arg, "..");
|
||||||
|
if (dotdot) {
|
||||||
|
unsigned char from_sha1[20];
|
||||||
|
char *next = dotdot + 2;
|
||||||
|
*dotdot = 0;
|
||||||
|
if (!*next)
|
||||||
|
next = "HEAD";
|
||||||
|
if (!get_sha1(arg, from_sha1) && !get_sha1(next, sha1)) {
|
||||||
|
struct commit *exclude;
|
||||||
|
struct commit *include;
|
||||||
|
|
||||||
|
exclude = get_commit_reference(revs, arg, from_sha1, flags ^ UNINTERESTING);
|
||||||
|
include = get_commit_reference(revs, next, sha1, flags);
|
||||||
|
if (!exclude || !include)
|
||||||
|
die("Invalid revision range %s..%s", arg, next);
|
||||||
|
add_one_commit(exclude, revs);
|
||||||
|
add_one_commit(include, revs);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*dotdot = '.';
|
||||||
|
}
|
||||||
|
local_flags = 0;
|
||||||
|
if (*arg == '^') {
|
||||||
|
local_flags = UNINTERESTING;
|
||||||
|
arg++;
|
||||||
|
}
|
||||||
|
if (get_sha1(arg, sha1) < 0) {
|
||||||
|
struct stat st;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
if (seen_dashdash || local_flags)
|
||||||
|
die("bad revision '%s'", arg);
|
||||||
|
|
||||||
|
/* If we didn't have a "--", all filenames must exist */
|
||||||
|
for (j = i; j < argc; j++) {
|
||||||
|
if (lstat(argv[j], &st) < 0)
|
||||||
|
die("'%s': %s", arg, strerror(errno));
|
||||||
|
}
|
||||||
|
revs->paths = get_pathspec(revs->prefix, argv + i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
commit = get_commit_reference(revs, arg, sha1, flags ^ local_flags);
|
||||||
|
add_one_commit(commit, revs);
|
||||||
|
}
|
||||||
|
if (def && !revs->commits) {
|
||||||
|
unsigned char sha1[20];
|
||||||
|
struct commit *commit;
|
||||||
|
if (get_sha1(def, sha1) < 0)
|
||||||
|
die("bad default revision '%s'", def);
|
||||||
|
commit = get_commit_reference(revs, def, sha1, 0);
|
||||||
|
add_one_commit(commit, revs);
|
||||||
|
}
|
||||||
|
if (revs->paths)
|
||||||
|
revs->limited = 1;
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepare_revision_walk(struct rev_info *revs)
|
||||||
|
{
|
||||||
|
sort_by_date(&revs->commits);
|
||||||
|
if (revs->limited)
|
||||||
|
limit_list(revs);
|
||||||
|
if (revs->topo_order)
|
||||||
|
sort_in_topological_order(&revs->commits, revs->lifo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rewrite_one(struct commit **pp)
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
struct commit *p = *pp;
|
||||||
|
if (p->object.flags & (TREECHANGE | UNINTERESTING))
|
||||||
|
return 0;
|
||||||
|
if (!p->parents)
|
||||||
|
return -1;
|
||||||
|
*pp = p->parents->item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rewrite_parents(struct commit *commit)
|
||||||
|
{
|
||||||
|
struct commit_list **pp = &commit->parents;
|
||||||
|
while (*pp) {
|
||||||
|
struct commit_list *parent = *pp;
|
||||||
|
if (rewrite_one(&parent->item) < 0) {
|
||||||
|
*pp = parent->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pp = &parent->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct commit *get_revision(struct rev_info *revs)
|
||||||
|
{
|
||||||
|
struct commit_list *list = revs->commits;
|
||||||
|
struct commit *commit;
|
||||||
|
|
||||||
|
if (!list)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Check the max_count ... */
|
||||||
|
commit = list->item;
|
||||||
|
switch (revs->max_count) {
|
||||||
|
case -1:
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
return NULL;
|
||||||
|
default:
|
||||||
|
revs->max_count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
commit = pop_most_recent_commit(&revs->commits, SEEN);
|
||||||
|
if (commit->object.flags & (UNINTERESTING|SHOWN))
|
||||||
|
continue;
|
||||||
|
if (revs->min_age != -1 && (commit->date > revs->min_age))
|
||||||
|
continue;
|
||||||
|
if (revs->max_age != -1 && (commit->date < revs->max_age))
|
||||||
|
return NULL;
|
||||||
|
if (revs->no_merges && commit->parents && commit->parents->next)
|
||||||
|
continue;
|
||||||
|
if (revs->paths && revs->dense) {
|
||||||
|
if (!(commit->object.flags & TREECHANGE))
|
||||||
|
continue;
|
||||||
|
rewrite_parents(commit);
|
||||||
|
}
|
||||||
|
commit->object.flags |= SHOWN;
|
||||||
|
return commit;
|
||||||
|
} while (revs->commits);
|
||||||
|
return NULL;
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
#ifndef REVISION_H
|
||||||
|
#define REVISION_H
|
||||||
|
|
||||||
|
#define SEEN (1u<<0)
|
||||||
|
#define UNINTERESTING (1u<<1)
|
||||||
|
#define TREECHANGE (1u<<2)
|
||||||
|
#define SHOWN (1u<<3)
|
||||||
|
|
||||||
|
struct rev_info {
|
||||||
|
/* Starting list */
|
||||||
|
struct commit_list *commits;
|
||||||
|
struct object_list *pending_objects;
|
||||||
|
|
||||||
|
/* Basic information */
|
||||||
|
const char *prefix;
|
||||||
|
const char **paths;
|
||||||
|
|
||||||
|
/* Traversal flags */
|
||||||
|
unsigned int dense:1,
|
||||||
|
no_merges:1,
|
||||||
|
remove_empty_trees:1,
|
||||||
|
lifo:1,
|
||||||
|
topo_order:1,
|
||||||
|
tag_objects:1,
|
||||||
|
tree_objects:1,
|
||||||
|
blob_objects:1,
|
||||||
|
edge_hint:1,
|
||||||
|
limited:1,
|
||||||
|
unpacked:1;
|
||||||
|
|
||||||
|
/* special limits */
|
||||||
|
int max_count;
|
||||||
|
unsigned long max_age;
|
||||||
|
unsigned long min_age;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* revision.c */
|
||||||
|
extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);
|
||||||
|
extern void prepare_revision_walk(struct rev_info *revs);
|
||||||
|
extern struct commit *get_revision(struct rev_info *revs);
|
||||||
|
|
||||||
|
extern void mark_parents_uninteresting(struct commit *commit);
|
||||||
|
extern void mark_tree_uninteresting(struct tree *tree);
|
||||||
|
|
||||||
|
struct name_path {
|
||||||
|
struct name_path *up;
|
||||||
|
int elem_len;
|
||||||
|
const char *elem;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct object_list **add_object(struct object *obj,
|
||||||
|
struct object_list **p,
|
||||||
|
struct name_path *path,
|
||||||
|
const char *name);
|
||||||
|
|
||||||
|
#endif
|
48
send-pack.c
48
send-pack.c
|
@ -12,6 +12,7 @@ static const char *exec = "git-receive-pack";
|
||||||
static int verbose = 0;
|
static int verbose = 0;
|
||||||
static int send_all = 0;
|
static int send_all = 0;
|
||||||
static int force_update = 0;
|
static int force_update = 0;
|
||||||
|
static int use_thin_pack = 0;
|
||||||
|
|
||||||
static int is_zero_sha1(const unsigned char *sha1)
|
static int is_zero_sha1(const unsigned char *sha1)
|
||||||
{
|
{
|
||||||
|
@ -37,26 +38,47 @@ static void exec_pack_objects(void)
|
||||||
|
|
||||||
static void exec_rev_list(struct ref *refs)
|
static void exec_rev_list(struct ref *refs)
|
||||||
{
|
{
|
||||||
|
struct ref *ref;
|
||||||
static char *args[1000];
|
static char *args[1000];
|
||||||
int i = 0;
|
int i = 0, j;
|
||||||
|
|
||||||
args[i++] = "rev-list"; /* 0 */
|
args[i++] = "rev-list"; /* 0 */
|
||||||
args[i++] = "--objects"; /* 1 */
|
if (use_thin_pack) /* 1 */
|
||||||
while (refs) {
|
args[i++] = "--objects-edge";
|
||||||
char *buf = malloc(100);
|
else
|
||||||
if (i > 900)
|
args[i++] = "--objects";
|
||||||
|
|
||||||
|
/* First send the ones we care about most */
|
||||||
|
for (ref = refs; ref; ref = ref->next) {
|
||||||
|
if (900 < i)
|
||||||
die("git-rev-list environment overflow");
|
die("git-rev-list environment overflow");
|
||||||
if (!is_zero_sha1(refs->old_sha1) &&
|
if (!is_zero_sha1(ref->new_sha1)) {
|
||||||
has_sha1_file(refs->old_sha1)) {
|
char *buf = malloc(100);
|
||||||
args[i++] = buf;
|
args[i++] = buf;
|
||||||
snprintf(buf, 50, "^%s", sha1_to_hex(refs->old_sha1));
|
snprintf(buf, 50, "%s", sha1_to_hex(ref->new_sha1));
|
||||||
buf += 50;
|
buf += 50;
|
||||||
|
if (!is_zero_sha1(ref->old_sha1) &&
|
||||||
|
has_sha1_file(ref->old_sha1)) {
|
||||||
|
args[i++] = buf;
|
||||||
|
snprintf(buf, 50, "^%s",
|
||||||
|
sha1_to_hex(ref->old_sha1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!is_zero_sha1(refs->new_sha1)) {
|
}
|
||||||
|
|
||||||
|
/* Then a handful of the remainder
|
||||||
|
* NEEDSWORK: we would be better off if used the newer ones first.
|
||||||
|
*/
|
||||||
|
for (ref = refs, j = i + 16;
|
||||||
|
i < 900 && i < j && ref;
|
||||||
|
ref = ref->next) {
|
||||||
|
if (is_zero_sha1(ref->new_sha1) &&
|
||||||
|
!is_zero_sha1(ref->old_sha1) &&
|
||||||
|
has_sha1_file(ref->old_sha1)) {
|
||||||
|
char *buf = malloc(42);
|
||||||
args[i++] = buf;
|
args[i++] = buf;
|
||||||
snprintf(buf, 50, "%s", sha1_to_hex(refs->new_sha1));
|
snprintf(buf, 42, "^%s", sha1_to_hex(ref->old_sha1));
|
||||||
}
|
}
|
||||||
refs = refs->next;
|
|
||||||
}
|
}
|
||||||
args[i] = NULL;
|
args[i] = NULL;
|
||||||
execv_git_cmd(args);
|
execv_git_cmd(args);
|
||||||
|
@ -361,6 +383,10 @@ int main(int argc, char **argv)
|
||||||
verbose = 1;
|
verbose = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strcmp(arg, "--thin")) {
|
||||||
|
use_thin_pack = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
usage(send_pack_usage);
|
usage(send_pack_usage);
|
||||||
}
|
}
|
||||||
if (!dest) {
|
if (!dest) {
|
||||||
|
|
43
sha1_file.c
43
sha1_file.c
|
@ -247,6 +247,7 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
|
||||||
for ( ; cp < ep && *cp != sep; cp++)
|
for ( ; cp < ep && *cp != sep; cp++)
|
||||||
;
|
;
|
||||||
if (last != cp) {
|
if (last != cp) {
|
||||||
|
struct stat st;
|
||||||
struct alternate_object_database *alt;
|
struct alternate_object_database *alt;
|
||||||
/* 43 = 40-byte + 2 '/' + terminating NUL */
|
/* 43 = 40-byte + 2 '/' + terminating NUL */
|
||||||
int pfxlen = cp - last;
|
int pfxlen = cp - last;
|
||||||
|
@ -269,9 +270,19 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
memcpy(ent->base, last, pfxlen);
|
memcpy(ent->base, last, pfxlen);
|
||||||
|
|
||||||
ent->name = ent->base + pfxlen + 1;
|
ent->name = ent->base + pfxlen + 1;
|
||||||
ent->base[pfxlen] = ent->base[pfxlen + 3] = '/';
|
ent->base[pfxlen + 3] = '/';
|
||||||
ent->base[entlen-1] = 0;
|
ent->base[pfxlen] = ent->base[entlen-1] = 0;
|
||||||
|
|
||||||
|
/* Detect cases where alternate disappeared */
|
||||||
|
if (stat(ent->base, &st) || !S_ISDIR(st.st_mode)) {
|
||||||
|
error("object directory %s does not exist; "
|
||||||
|
"check .git/objects/info/alternates.",
|
||||||
|
ent->base);
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
ent->base[pfxlen] = '/';
|
||||||
|
|
||||||
/* Prevent the common mistake of listing the same
|
/* Prevent the common mistake of listing the same
|
||||||
* thing twice, or object directory itself.
|
* thing twice, or object directory itself.
|
||||||
|
@ -552,7 +563,9 @@ static void prepare_packed_git_one(char *objdir, int local)
|
||||||
len = strlen(path);
|
len = strlen(path);
|
||||||
dir = opendir(path);
|
dir = opendir(path);
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
fprintf(stderr, "unable to open object pack directory: %s: %s\n", path, strerror(errno));
|
if (errno != ENOENT)
|
||||||
|
error("unable to open object pack directory: %s: %s",
|
||||||
|
path, strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
path[len++] = '/';
|
path[len++] = '/';
|
||||||
|
@ -828,6 +841,25 @@ static unsigned long unpack_object_header(struct packed_git *p, unsigned long of
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int check_reuse_pack_delta(struct packed_git *p, unsigned long offset,
|
||||||
|
unsigned char *base, unsigned long *sizep,
|
||||||
|
enum object_type *kindp)
|
||||||
|
{
|
||||||
|
unsigned long ptr;
|
||||||
|
int status = -1;
|
||||||
|
|
||||||
|
use_packed_git(p);
|
||||||
|
ptr = offset;
|
||||||
|
ptr = unpack_object_header(p, ptr, kindp, sizep);
|
||||||
|
if (*kindp != OBJ_DELTA)
|
||||||
|
goto done;
|
||||||
|
memcpy(base, p->pack_base + ptr, 20);
|
||||||
|
status = 0;
|
||||||
|
done:
|
||||||
|
unuse_packed_git(p);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
void packed_object_info_detail(struct pack_entry *e,
|
void packed_object_info_detail(struct pack_entry *e,
|
||||||
char *type,
|
char *type,
|
||||||
unsigned long *size,
|
unsigned long *size,
|
||||||
|
@ -1481,7 +1513,8 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
|
||||||
|
|
||||||
local = mkstemp(tmpfile);
|
local = mkstemp(tmpfile);
|
||||||
if (local < 0)
|
if (local < 0)
|
||||||
return error("Couldn't open %s for %s\n", tmpfile, sha1_to_hex(sha1));
|
return error("Couldn't open %s for %s",
|
||||||
|
tmpfile, sha1_to_hex(sha1));
|
||||||
|
|
||||||
memset(&stream, 0, sizeof(stream));
|
memset(&stream, 0, sizeof(stream));
|
||||||
|
|
||||||
|
@ -1529,7 +1562,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
|
||||||
}
|
}
|
||||||
if (memcmp(sha1, real_sha1, 20)) {
|
if (memcmp(sha1, real_sha1, 20)) {
|
||||||
unlink(tmpfile);
|
unlink(tmpfile);
|
||||||
return error("File %s has bad hash\n", sha1_to_hex(sha1));
|
return error("File %s has bad hash", sha1_to_hex(sha1));
|
||||||
}
|
}
|
||||||
|
|
||||||
return move_temp_to_file(tmpfile, sha1_file_name(sha1));
|
return move_temp_to_file(tmpfile, sha1_file_name(sha1));
|
||||||
|
|
|
@ -8,10 +8,7 @@ SHELL_PATH ?= $(SHELL)
|
||||||
TAR ?= $(TAR)
|
TAR ?= $(TAR)
|
||||||
|
|
||||||
# Shell quote;
|
# Shell quote;
|
||||||
# Result of this needs to be placed inside ''
|
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
|
||||||
shq = $(subst ','\'',$(1))
|
|
||||||
# This has surrounding ''
|
|
||||||
shellquote = '$(call shq,$(1))'
|
|
||||||
|
|
||||||
T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
|
T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
|
||||||
|
|
||||||
|
@ -22,7 +19,7 @@ endif
|
||||||
all: $(T) clean
|
all: $(T) clean
|
||||||
|
|
||||||
$(T):
|
$(T):
|
||||||
@echo "*** $@ ***"; $(call shellquote,$(SHELL_PATH)) $@ $(GIT_TEST_OPTS)
|
@echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -fr trash
|
rm -fr trash
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Copyright (c) 2006 Carl D. Worth
|
||||||
|
#
|
||||||
|
|
||||||
|
test_description='git-ls-files test for --error-unmatch option
|
||||||
|
|
||||||
|
This test runs git-ls-files --error-unmatch to ensure it correctly
|
||||||
|
returns an error when a non-existent path is provided on the command
|
||||||
|
line.
|
||||||
|
'
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
touch foo bar
|
||||||
|
git-update-index --add foo bar
|
||||||
|
git-commit -m "add foo bar"
|
||||||
|
|
||||||
|
test_expect_failure \
|
||||||
|
'git-ls-files --error-unmatch should fail with unmatched path.' \
|
||||||
|
'git-ls-files --error-unmatch foo bar-does-not-match'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'git-ls-files --error-unmatch should succeed eith matched paths.' \
|
||||||
|
'git-ls-files --error-unmatch foo bar'
|
||||||
|
|
||||||
|
test_done
|
||||||
|
1
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Copyright (c) 2006 Carl D. Worth
|
||||||
|
#
|
||||||
|
|
||||||
|
test_description='Test of the various options to git-rm.'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
# Setup some files to be removed, some with funny characters
|
||||||
|
touch -- foo bar baz 'space embedded' 'tab embedded' 'newline
|
||||||
|
embedded' -q
|
||||||
|
git-add -- foo bar baz 'space embedded' 'tab embedded' 'newline
|
||||||
|
embedded' -q
|
||||||
|
git-commit -m "add files"
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'Pre-check that foo exists and is in index before git-rm foo' \
|
||||||
|
'[ -f foo ] && git-ls-files --error-unmatch foo'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'Test that git-rm foo succeeds' \
|
||||||
|
'git-rm foo'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'Post-check that foo exists but is not in index after git-rm foo' \
|
||||||
|
'[ -f foo ] && ! git-ls-files --error-unmatch foo'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'Pre-check that bar exists and is in index before "git-rm -f bar"' \
|
||||||
|
'[ -f bar ] && git-ls-files --error-unmatch bar'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'Test that "git-rm -f bar" succeeds' \
|
||||||
|
'git-rm -f bar'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'Post-check that bar does not exist and is not in index after "git-rm -f bar"' \
|
||||||
|
'! [ -f bar ] && ! git-ls-files --error-unmatch bar'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'Test that "git-rm -- -q" succeeds (remove a file that looks like an option)' \
|
||||||
|
'git-rm -- -q'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
"Test that \"git-rm -f\" succeeds with embedded space, tab, or newline characters." \
|
||||||
|
"git-rm -f 'space embedded' 'tab embedded' 'newline
|
||||||
|
embedded'"
|
||||||
|
|
||||||
|
chmod u-w .
|
||||||
|
test_expect_failure \
|
||||||
|
'Test that "git-rm -f" fails if its rm fails' \
|
||||||
|
'git-rm -f baz'
|
||||||
|
chmod u+w .
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'When the rm in "git-rm -f" fails, it should not remove the file from the index' \
|
||||||
|
'git-ls-files --error-unmatch baz'
|
||||||
|
|
||||||
|
test_done
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Copyright (c) 2006 Carl D. Worth
|
||||||
|
#
|
||||||
|
|
||||||
|
test_description='Test of git-add, including the -- option.'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'Test of git-add' \
|
||||||
|
'touch foo && git-add foo'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'Post-check that foo is in the index' \
|
||||||
|
'git-ls-files foo | grep foo'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'Test that "git-add -- -q" works' \
|
||||||
|
'touch -- -q && git-add -- -q'
|
||||||
|
|
||||||
|
test_done
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Copyright (C) 2006 Carl D. Worth <cworth@cworth.org>
|
||||||
|
#
|
||||||
|
|
||||||
|
test_description='test git-clone to cleanup after failure
|
||||||
|
|
||||||
|
This test covers the fact that if git-clone fails, it should remove
|
||||||
|
the directory it created, to avoid the user having to manually
|
||||||
|
remove the directory before attempting a clone again.'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
test_expect_failure \
|
||||||
|
'clone of non-existent source should fail' \
|
||||||
|
'git-clone foo bar'
|
||||||
|
|
||||||
|
test_expect_failure \
|
||||||
|
'failed clone should not leave a directory' \
|
||||||
|
'cd bar'
|
||||||
|
|
||||||
|
# Need a repo to clone
|
||||||
|
test_create_repo foo
|
||||||
|
|
||||||
|
# clone doesn't like it if there is no HEAD. Is that a bug?
|
||||||
|
(cd foo && touch file && git add file && git commit -m 'add file' >/dev/null 2>&1)
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'clone should work now that source exists' \
|
||||||
|
'git-clone foo bar'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'successfull clone must leave the directory' \
|
||||||
|
'cd bar'
|
||||||
|
|
||||||
|
test_done
|
|
@ -1,462 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# Copyright (c) 2005 Jon Seymour
|
|
||||||
#
|
|
||||||
|
|
||||||
test_description='Tests git-rev-list --merge-order functionality'
|
|
||||||
|
|
||||||
. ./test-lib.sh
|
|
||||||
. ../t6000lib.sh # t6xxx specific functions
|
|
||||||
|
|
||||||
# test-case specific test function
|
|
||||||
check_adjacency()
|
|
||||||
{
|
|
||||||
read previous
|
|
||||||
echo "= $previous"
|
|
||||||
while read next
|
|
||||||
do
|
|
||||||
if ! (git-cat-file commit $previous | grep "^parent $next" >/dev/null)
|
|
||||||
then
|
|
||||||
echo "^ $next"
|
|
||||||
else
|
|
||||||
echo "| $next"
|
|
||||||
fi
|
|
||||||
previous=$next
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
list_duplicates()
|
|
||||||
{
|
|
||||||
"$@" | sort | uniq -d
|
|
||||||
}
|
|
||||||
|
|
||||||
grep_stderr()
|
|
||||||
{
|
|
||||||
args=$1
|
|
||||||
shift 1
|
|
||||||
"$@" 2>&1 | grep "$args"
|
|
||||||
}
|
|
||||||
|
|
||||||
date >path0
|
|
||||||
git-update-index --add path0
|
|
||||||
save_tag tree git-write-tree
|
|
||||||
on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
|
|
||||||
on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
|
|
||||||
on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
|
|
||||||
on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1
|
|
||||||
on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2
|
|
||||||
on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0
|
|
||||||
on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0
|
|
||||||
on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1
|
|
||||||
on_committer_date "1971-08-16 00:00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1
|
|
||||||
on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b2 tree -p b2
|
|
||||||
on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
|
|
||||||
on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2
|
|
||||||
on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1
|
|
||||||
on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2
|
|
||||||
on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
|
|
||||||
on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
|
|
||||||
on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
|
|
||||||
on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
|
|
||||||
on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
|
|
||||||
on_committer_date "1971-08-16 00:00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3
|
|
||||||
on_committer_date "1971-08-16 00:00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4
|
|
||||||
on_committer_date "1971-08-16 00:00:21" hide_error save_tag alt_root unique_commit alt_root tree
|
|
||||||
on_committer_date "1971-08-16 00:00:22" save_tag r0 unique_commit r0 tree -p alt_root
|
|
||||||
on_committer_date "1971-08-16 00:00:23" save_tag r1 unique_commit r1 tree -p r0
|
|
||||||
on_committer_date "1971-08-16 00:00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1
|
|
||||||
on_committer_date "1971-08-16 00:00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# note: as of 20/6, it isn't possible to create duplicate parents, so this
|
|
||||||
# can't be tested.
|
|
||||||
#
|
|
||||||
#on_committer_date "1971-08-16 00:00:20" save_tag m3 unique_commit m3 tree -p c3 -p a4 -p c3
|
|
||||||
hide_error save_tag e1 as_author e@example.com unique_commit e1 tree
|
|
||||||
save_tag e2 as_author e@example.com unique_commit e2 tree -p e1
|
|
||||||
save_tag f1 as_author f@example.com unique_commit f1 tree -p e1
|
|
||||||
save_tag e3 as_author e@example.com unique_commit e3 tree -p e2
|
|
||||||
save_tag f2 as_author f@example.com unique_commit f2 tree -p f1
|
|
||||||
save_tag e4 as_author e@example.com unique_commit e4 tree -p e3 -p f2
|
|
||||||
save_tag e5 as_author e@example.com unique_commit e5 tree -p e4
|
|
||||||
save_tag f3 as_author f@example.com unique_commit f3 tree -p f2
|
|
||||||
save_tag f4 as_author f@example.com unique_commit f4 tree -p f3
|
|
||||||
save_tag e6 as_author e@example.com unique_commit e6 tree -p e5 -p f4
|
|
||||||
save_tag f5 as_author f@example.com unique_commit f5 tree -p f4
|
|
||||||
save_tag f6 as_author f@example.com unique_commit f6 tree -p f5 -p e6
|
|
||||||
save_tag e7 as_author e@example.com unique_commit e7 tree -p e6
|
|
||||||
save_tag e8 as_author e@example.com unique_commit e8 tree -p e7
|
|
||||||
save_tag e9 as_author e@example.com unique_commit e9 tree -p e8
|
|
||||||
save_tag f7 as_author f@example.com unique_commit f7 tree -p f6
|
|
||||||
save_tag f8 as_author f@example.com unique_commit f8 tree -p f7
|
|
||||||
save_tag f9 as_author f@example.com unique_commit f9 tree -p f8
|
|
||||||
save_tag e10 as_author e@example.com unique_commit e1 tree -p e9 -p f8
|
|
||||||
|
|
||||||
hide_error save_tag g0 unique_commit g0 tree
|
|
||||||
save_tag g1 unique_commit g1 tree -p g0
|
|
||||||
save_tag h1 unique_commit g2 tree -p g0
|
|
||||||
save_tag g2 unique_commit g3 tree -p g1 -p h1
|
|
||||||
save_tag h2 unique_commit g4 tree -p g2
|
|
||||||
save_tag g3 unique_commit g5 tree -p g2
|
|
||||||
save_tag g4 unique_commit g6 tree -p g3 -p h2
|
|
||||||
|
|
||||||
git-update-ref HEAD $(tag l5)
|
|
||||||
|
|
||||||
test_output_expect_success 'rev-list has correct number of entries' 'git-rev-list HEAD | wc -l | tr -d \" \"' <<EOF
|
|
||||||
19
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if git-rev-list --merge-order HEAD 2>&1 | grep 'OpenSSL not linked' >/dev/null
|
|
||||||
then
|
|
||||||
test_expect_success 'skipping merge-order test' :
|
|
||||||
test_done
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
normal_adjacency_count=$(git-rev-list HEAD | check_adjacency | grep -c "\^" | tr -d ' ')
|
|
||||||
merge_order_adjacency_count=$(git-rev-list --merge-order HEAD | check_adjacency | grep -c "\^" | tr -d ' ')
|
|
||||||
test_expect_success '--merge-order produces as many or fewer discontinuities' '[ $merge_order_adjacency_count -le $normal_adjacency_count ]'
|
|
||||||
test_output_expect_success 'simple merge order' 'git-rev-list --merge-order --show-breaks HEAD' <<EOF
|
|
||||||
= l5
|
|
||||||
| l4
|
|
||||||
| l3
|
|
||||||
= a4
|
|
||||||
| c3
|
|
||||||
| c2
|
|
||||||
| c1
|
|
||||||
^ b4
|
|
||||||
| b3
|
|
||||||
| b2
|
|
||||||
| b1
|
|
||||||
^ a3
|
|
||||||
| a2
|
|
||||||
| a1
|
|
||||||
= a0
|
|
||||||
| l2
|
|
||||||
| l1
|
|
||||||
| l0
|
|
||||||
= root
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success 'two diamonds merge order (g6)' 'git-rev-list --merge-order --show-breaks g4' <<EOF
|
|
||||||
= g4
|
|
||||||
| h2
|
|
||||||
^ g3
|
|
||||||
= g2
|
|
||||||
| h1
|
|
||||||
^ g1
|
|
||||||
= g0
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success 'multiple heads' 'git-rev-list --merge-order a3 b3 c3' <<EOF
|
|
||||||
c3
|
|
||||||
c2
|
|
||||||
c1
|
|
||||||
b3
|
|
||||||
b2
|
|
||||||
b1
|
|
||||||
a3
|
|
||||||
a2
|
|
||||||
a1
|
|
||||||
a0
|
|
||||||
l2
|
|
||||||
l1
|
|
||||||
l0
|
|
||||||
root
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success 'multiple heads, prune at a1' 'git-rev-list --merge-order a3 b3 c3 ^a1' <<EOF
|
|
||||||
c3
|
|
||||||
c2
|
|
||||||
c1
|
|
||||||
b3
|
|
||||||
b2
|
|
||||||
b1
|
|
||||||
a3
|
|
||||||
a2
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success 'multiple heads, prune at l1' 'git-rev-list --merge-order a3 b3 c3 ^l1' <<EOF
|
|
||||||
c3
|
|
||||||
c2
|
|
||||||
c1
|
|
||||||
b3
|
|
||||||
b2
|
|
||||||
b1
|
|
||||||
a3
|
|
||||||
a2
|
|
||||||
a1
|
|
||||||
a0
|
|
||||||
l2
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success 'cross-epoch, head at l5, prune at l1' 'git-rev-list --merge-order l5 ^l1' <<EOF
|
|
||||||
l5
|
|
||||||
l4
|
|
||||||
l3
|
|
||||||
a4
|
|
||||||
c3
|
|
||||||
c2
|
|
||||||
c1
|
|
||||||
b4
|
|
||||||
b3
|
|
||||||
b2
|
|
||||||
b1
|
|
||||||
a3
|
|
||||||
a2
|
|
||||||
a1
|
|
||||||
a0
|
|
||||||
l2
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success 'duplicated head arguments' 'git-rev-list --merge-order l5 l5 ^l1' <<EOF
|
|
||||||
l5
|
|
||||||
l4
|
|
||||||
l3
|
|
||||||
a4
|
|
||||||
c3
|
|
||||||
c2
|
|
||||||
c1
|
|
||||||
b4
|
|
||||||
b3
|
|
||||||
b2
|
|
||||||
b1
|
|
||||||
a3
|
|
||||||
a2
|
|
||||||
a1
|
|
||||||
a0
|
|
||||||
l2
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success 'prune near merge' 'git-rev-list --merge-order a4 ^c3' <<EOF
|
|
||||||
a4
|
|
||||||
b4
|
|
||||||
b3
|
|
||||||
a3
|
|
||||||
a2
|
|
||||||
a1
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success "head has no parent" 'git-rev-list --merge-order --show-breaks root' <<EOF
|
|
||||||
= root
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success "two nodes - one head, one base" 'git-rev-list --merge-order --show-breaks l0' <<EOF
|
|
||||||
= l0
|
|
||||||
= root
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success "three nodes one head, one internal, one base" 'git-rev-list --merge-order --show-breaks l1' <<EOF
|
|
||||||
= l1
|
|
||||||
| l0
|
|
||||||
= root
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success "linear prune l2 ^root" 'git-rev-list --merge-order --show-breaks l2 ^root' <<EOF
|
|
||||||
^ l2
|
|
||||||
| l1
|
|
||||||
| l0
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success "linear prune l2 ^l0" 'git-rev-list --merge-order --show-breaks l2 ^l0' <<EOF
|
|
||||||
^ l2
|
|
||||||
| l1
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success "linear prune l2 ^l1" 'git-rev-list --merge-order --show-breaks l2 ^l1' <<EOF
|
|
||||||
^ l2
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success "linear prune l5 ^a4" 'git-rev-list --merge-order --show-breaks l5 ^a4' <<EOF
|
|
||||||
^ l5
|
|
||||||
| l4
|
|
||||||
| l3
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success "linear prune l5 ^l3" 'git-rev-list --merge-order --show-breaks l5 ^l3' <<EOF
|
|
||||||
^ l5
|
|
||||||
| l4
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success "linear prune l5 ^l4" 'git-rev-list --merge-order --show-breaks l5 ^l4' <<EOF
|
|
||||||
^ l5
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success "max-count 10 - merge order" 'git-rev-list --merge-order --show-breaks --max-count=10 l5' <<EOF
|
|
||||||
= l5
|
|
||||||
| l4
|
|
||||||
| l3
|
|
||||||
= a4
|
|
||||||
| c3
|
|
||||||
| c2
|
|
||||||
| c1
|
|
||||||
^ b4
|
|
||||||
| b3
|
|
||||||
| b2
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success "max-count 10 - non merge order" 'git-rev-list --max-count=10 l5' <<EOF
|
|
||||||
l5
|
|
||||||
l4
|
|
||||||
l3
|
|
||||||
a4
|
|
||||||
b4
|
|
||||||
a3
|
|
||||||
a2
|
|
||||||
c3
|
|
||||||
c2
|
|
||||||
b3
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success '--max-age=c3, no --merge-order' "git-rev-list --max-age=$(commit_date c3) l5" <<EOF
|
|
||||||
l5
|
|
||||||
l4
|
|
||||||
l3
|
|
||||||
a4
|
|
||||||
b4
|
|
||||||
a3
|
|
||||||
a2
|
|
||||||
c3
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success '--max-age=c3, --merge-order' "git-rev-list --merge-order --max-age=$(commit_date c3) l5" <<EOF
|
|
||||||
l5
|
|
||||||
l4
|
|
||||||
l3
|
|
||||||
a4
|
|
||||||
c3
|
|
||||||
b4
|
|
||||||
a3
|
|
||||||
a2
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success 'one specified head reachable from another a4, c3, --merge-order' "list_duplicates git-rev-list --merge-order a4 c3" <<EOF
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success 'one specified head reachable from another c3, a4, --merge-order' "list_duplicates git-rev-list --merge-order c3 a4" <<EOF
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success 'one specified head reachable from another a4, c3, no --merge-order' "list_duplicates git-rev-list a4 c3" <<EOF
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success 'one specified head reachable from another c3, a4, no --merge-order' "list_duplicates git-rev-list c3 a4" <<EOF
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success 'graph with c3 and a4 parents of head' "list_duplicates git-rev-list m1" <<EOF
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success 'graph with a4 and c3 parents of head' "list_duplicates git-rev-list m2" <<EOF
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_expect_success "head ^head --merge-order" 'git-rev-list --merge-order --show-breaks a3 ^a3' <<EOF
|
|
||||||
EOF
|
|
||||||
|
|
||||||
#
|
|
||||||
# can't test this now - duplicate parents can't be created
|
|
||||||
#
|
|
||||||
#test_output_expect_success 'duplicate parents' 'git-rev-list --parents --merge-order --show-breaks m3' <<EOF
|
|
||||||
#= m3 c3 a4 c3
|
|
||||||
#| a4 c3 b4 a3
|
|
||||||
#| b4 a3 b3
|
|
||||||
#| b3 b2
|
|
||||||
#^ a3 a2
|
|
||||||
#| a2 a1
|
|
||||||
#| a1 a0
|
|
||||||
#^ c3 c2
|
|
||||||
#| c2 b2 c1
|
|
||||||
#| b2 b1
|
|
||||||
#^ c1 b1
|
|
||||||
#| b1 a0
|
|
||||||
#= a0 l2
|
|
||||||
#| l2 l1
|
|
||||||
#| l1 l0
|
|
||||||
#| l0 root
|
|
||||||
#= root
|
|
||||||
#EOF
|
|
||||||
|
|
||||||
test_expect_success "head ^head no --merge-order" 'git-rev-list a3 ^a3' <<EOF
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success 'simple merge order (l5r1)' 'git-rev-list --merge-order --show-breaks l5r1' <<EOF
|
|
||||||
= l5r1
|
|
||||||
| r1
|
|
||||||
| r0
|
|
||||||
| alt_root
|
|
||||||
^ l5
|
|
||||||
| l4
|
|
||||||
| l3
|
|
||||||
| a4
|
|
||||||
| c3
|
|
||||||
| c2
|
|
||||||
| c1
|
|
||||||
^ b4
|
|
||||||
| b3
|
|
||||||
| b2
|
|
||||||
| b1
|
|
||||||
^ a3
|
|
||||||
| a2
|
|
||||||
| a1
|
|
||||||
| a0
|
|
||||||
| l2
|
|
||||||
| l1
|
|
||||||
| l0
|
|
||||||
= root
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success 'simple merge order (r1l5)' 'git-rev-list --merge-order --show-breaks r1l5' <<EOF
|
|
||||||
= r1l5
|
|
||||||
| l5
|
|
||||||
| l4
|
|
||||||
| l3
|
|
||||||
| a4
|
|
||||||
| c3
|
|
||||||
| c2
|
|
||||||
| c1
|
|
||||||
^ b4
|
|
||||||
| b3
|
|
||||||
| b2
|
|
||||||
| b1
|
|
||||||
^ a3
|
|
||||||
| a2
|
|
||||||
| a1
|
|
||||||
| a0
|
|
||||||
| l2
|
|
||||||
| l1
|
|
||||||
| l0
|
|
||||||
| root
|
|
||||||
^ r1
|
|
||||||
| r0
|
|
||||||
= alt_root
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success "don't print things unreachable from one branch" "git-rev-list a3 ^b3 --merge-order" <<EOF
|
|
||||||
a3
|
|
||||||
a2
|
|
||||||
a1
|
|
||||||
EOF
|
|
||||||
|
|
||||||
test_output_expect_success "--merge-order a4 l3" "git-rev-list --merge-order a4 l3" <<EOF
|
|
||||||
l3
|
|
||||||
a4
|
|
||||||
c3
|
|
||||||
c2
|
|
||||||
c1
|
|
||||||
b4
|
|
||||||
b3
|
|
||||||
b2
|
|
||||||
b1
|
|
||||||
a3
|
|
||||||
a2
|
|
||||||
a1
|
|
||||||
a0
|
|
||||||
l2
|
|
||||||
l1
|
|
||||||
l0
|
|
||||||
root
|
|
||||||
EOF
|
|
||||||
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
test_done
|
|
|
@ -9,6 +9,7 @@
|
||||||
#
|
#
|
||||||
# To enable this hook, make this file executable.
|
# To enable this hook, make this file executable.
|
||||||
|
|
||||||
|
. git-sh-setup
|
||||||
test -x "$GIT_DIR/hooks/commit-msg" &&
|
test -x "$GIT_DIR/hooks/commit-msg" &&
|
||||||
exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
|
exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
|
||||||
:
|
:
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#
|
#
|
||||||
# To enable this hook, make this file executable.
|
# To enable this hook, make this file executable.
|
||||||
|
|
||||||
|
. git-sh-setup
|
||||||
test -x "$GIT_DIR/hooks/pre-commit" &&
|
test -x "$GIT_DIR/hooks/pre-commit" &&
|
||||||
exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
|
exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
|
||||||
:
|
:
|
||||||
|
|
|
@ -23,6 +23,10 @@ static int quiet; /* --refresh needing update is not error */
|
||||||
static int info_only;
|
static int info_only;
|
||||||
static int force_remove;
|
static int force_remove;
|
||||||
static int verbose;
|
static int verbose;
|
||||||
|
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 */
|
/* Three functions to allow overloaded pointer return; see linux/err.h */
|
||||||
static inline void *ERR_PTR(long error)
|
static inline void *ERR_PTR(long error)
|
||||||
|
@ -53,6 +57,25 @@ static void report(const char *fmt, ...)
|
||||||
va_end(vp);
|
va_end(vp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mark_valid(const char *path)
|
||||||
|
{
|
||||||
|
int namelen = strlen(path);
|
||||||
|
int pos = cache_name_pos(path, namelen);
|
||||||
|
if (0 <= pos) {
|
||||||
|
switch (mark_valid_only) {
|
||||||
|
case MARK_VALID:
|
||||||
|
active_cache[pos]->ce_flags |= htons(CE_VALID);
|
||||||
|
break;
|
||||||
|
case UNMARK_VALID:
|
||||||
|
active_cache[pos]->ce_flags &= ~htons(CE_VALID);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
active_cache_changed = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static int add_file_to_cache(const char *path)
|
static int add_file_to_cache(const char *path)
|
||||||
{
|
{
|
||||||
int size, namelen, option, status;
|
int size, namelen, option, status;
|
||||||
|
@ -94,6 +117,7 @@ static int add_file_to_cache(const char *path)
|
||||||
ce = xmalloc(size);
|
ce = xmalloc(size);
|
||||||
memset(ce, 0, size);
|
memset(ce, 0, size);
|
||||||
memcpy(ce->name, path, namelen);
|
memcpy(ce->name, path, namelen);
|
||||||
|
ce->ce_flags = htons(namelen);
|
||||||
fill_stat_cache_info(ce, &st);
|
fill_stat_cache_info(ce, &st);
|
||||||
|
|
||||||
ce->ce_mode = create_ce_mode(st.st_mode);
|
ce->ce_mode = create_ce_mode(st.st_mode);
|
||||||
|
@ -105,7 +129,6 @@ static int add_file_to_cache(const char *path)
|
||||||
if (0 <= pos)
|
if (0 <= pos)
|
||||||
ce->ce_mode = active_cache[pos]->ce_mode;
|
ce->ce_mode = active_cache[pos]->ce_mode;
|
||||||
}
|
}
|
||||||
ce->ce_flags = htons(namelen);
|
|
||||||
|
|
||||||
if (index_path(ce->sha1, path, &st, !info_only))
|
if (index_path(ce->sha1, path, &st, !info_only))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -128,7 +151,7 @@ static int add_file_to_cache(const char *path)
|
||||||
* For example, you'd want to do this after doing a "git-read-tree",
|
* 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.
|
* to link up the stat cache details with the proper files.
|
||||||
*/
|
*/
|
||||||
static struct cache_entry *refresh_entry(struct cache_entry *ce)
|
static struct cache_entry *refresh_entry(struct cache_entry *ce, int really)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
struct cache_entry *updated;
|
struct cache_entry *updated;
|
||||||
|
@ -137,21 +160,36 @@ static struct cache_entry *refresh_entry(struct cache_entry *ce)
|
||||||
if (lstat(ce->name, &st) < 0)
|
if (lstat(ce->name, &st) < 0)
|
||||||
return ERR_PTR(-errno);
|
return ERR_PTR(-errno);
|
||||||
|
|
||||||
changed = ce_match_stat(ce, &st);
|
changed = ce_match_stat(ce, &st, really);
|
||||||
if (!changed)
|
if (!changed) {
|
||||||
return NULL;
|
if (really && assume_unchanged &&
|
||||||
|
!(ce->ce_flags & htons(CE_VALID)))
|
||||||
|
; /* mark this one VALID again */
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (ce_modified(ce, &st))
|
if (ce_modified(ce, &st, really))
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
size = ce_size(ce);
|
size = ce_size(ce);
|
||||||
updated = xmalloc(size);
|
updated = xmalloc(size);
|
||||||
memcpy(updated, ce, size);
|
memcpy(updated, ce, size);
|
||||||
fill_stat_cache_info(updated, &st);
|
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;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int refresh_cache(void)
|
static int refresh_cache(int really)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int has_errors = 0;
|
int has_errors = 0;
|
||||||
|
@ -171,12 +209,19 @@ static int refresh_cache(void)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
new = refresh_entry(ce);
|
new = refresh_entry(ce, really);
|
||||||
if (!new)
|
if (!new)
|
||||||
continue;
|
continue;
|
||||||
if (IS_ERR(new)) {
|
if (IS_ERR(new)) {
|
||||||
if (not_new && PTR_ERR(new) == -ENOENT)
|
if (not_new && PTR_ERR(new) == -ENOENT)
|
||||||
continue;
|
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)
|
if (quiet)
|
||||||
continue;
|
continue;
|
||||||
printf("%s: needs update\n", ce->name);
|
printf("%s: needs update\n", ce->name);
|
||||||
|
@ -274,6 +319,8 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
|
||||||
memcpy(ce->name, path, len);
|
memcpy(ce->name, path, len);
|
||||||
ce->ce_flags = create_ce_flags(len, stage);
|
ce->ce_flags = create_ce_flags(len, stage);
|
||||||
ce->ce_mode = create_ce_mode(mode);
|
ce->ce_mode = create_ce_mode(mode);
|
||||||
|
if (assume_unchanged)
|
||||||
|
ce->ce_flags |= htons(CE_VALID);
|
||||||
option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
|
option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
|
||||||
option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
|
option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
|
||||||
if (add_cache_entry(ce, option))
|
if (add_cache_entry(ce, option))
|
||||||
|
@ -317,6 +364,12 @@ static void update_one(const char *path, const char *prefix, int prefix_length)
|
||||||
fprintf(stderr, "Ignoring path %s\n", path);
|
fprintf(stderr, "Ignoring path %s\n", path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (mark_valid_only) {
|
||||||
|
if (mark_valid(p))
|
||||||
|
die("Unable to mark file %s", path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (force_remove) {
|
if (force_remove) {
|
||||||
if (remove_file_from_cache(p))
|
if (remove_file_from_cache(p))
|
||||||
die("git-update-index: unable to remove %s", path);
|
die("git-update-index: unable to remove %s", path);
|
||||||
|
@ -467,7 +520,11 @@ int main(int argc, const char **argv)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(path, "--refresh")) {
|
if (!strcmp(path, "--refresh")) {
|
||||||
has_errors |= refresh_cache();
|
has_errors |= refresh_cache(0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(path, "--really-refresh")) {
|
||||||
|
has_errors |= refresh_cache(1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(path, "--cacheinfo")) {
|
if (!strcmp(path, "--cacheinfo")) {
|
||||||
|
@ -493,6 +550,14 @@ int main(int argc, const char **argv)
|
||||||
die("git-update-index: %s cannot chmod %s", path, argv[i]);
|
die("git-update-index: %s cannot chmod %s", path, argv[i]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strcmp(path, "--assume-unchanged")) {
|
||||||
|
mark_valid_only = MARK_VALID;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(path, "--no-assume-unchanged")) {
|
||||||
|
mark_valid_only = UNMARK_VALID;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!strcmp(path, "--info-only")) {
|
if (!strcmp(path, "--info-only")) {
|
||||||
info_only = 1;
|
info_only = 1;
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -14,6 +14,7 @@ static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=n
|
||||||
#define MAX_HAS 256
|
#define MAX_HAS 256
|
||||||
#define MAX_NEEDS 256
|
#define MAX_NEEDS 256
|
||||||
static int nr_has = 0, nr_needs = 0, multi_ack = 0, nr_our_refs = 0;
|
static int nr_has = 0, nr_needs = 0, multi_ack = 0, nr_our_refs = 0;
|
||||||
|
static int use_thin_pack = 0;
|
||||||
static unsigned char has_sha1[MAX_HAS][20];
|
static unsigned char has_sha1[MAX_HAS][20];
|
||||||
static unsigned char needs_sha1[MAX_NEEDS][20];
|
static unsigned char needs_sha1[MAX_NEEDS][20];
|
||||||
static unsigned int timeout = 0;
|
static unsigned int timeout = 0;
|
||||||
|
@ -49,8 +50,10 @@ static void create_pack_file(void)
|
||||||
char *buf;
|
char *buf;
|
||||||
char **p;
|
char **p;
|
||||||
|
|
||||||
if (create_full_pack)
|
if (create_full_pack) {
|
||||||
args = 10;
|
args = 10;
|
||||||
|
use_thin_pack = 0; /* no point doing it */
|
||||||
|
}
|
||||||
else
|
else
|
||||||
args = nr_has + nr_needs + 5;
|
args = nr_has + nr_needs + 5;
|
||||||
argv = xmalloc(args * sizeof(char *));
|
argv = xmalloc(args * sizeof(char *));
|
||||||
|
@ -62,7 +65,7 @@ static void create_pack_file(void)
|
||||||
close(fd[0]);
|
close(fd[0]);
|
||||||
close(fd[1]);
|
close(fd[1]);
|
||||||
*p++ = "rev-list";
|
*p++ = "rev-list";
|
||||||
*p++ = "--objects";
|
*p++ = use_thin_pack ? "--objects-edge" : "--objects";
|
||||||
if (create_full_pack || MAX_NEEDS <= nr_needs)
|
if (create_full_pack || MAX_NEEDS <= nr_needs)
|
||||||
*p++ = "--all";
|
*p++ = "--all";
|
||||||
else {
|
else {
|
||||||
|
@ -192,6 +195,8 @@ static int receive_needs(void)
|
||||||
"expected to get sha, not '%s'", line);
|
"expected to get sha, not '%s'", line);
|
||||||
if (strstr(line+45, "multi_ack"))
|
if (strstr(line+45, "multi_ack"))
|
||||||
multi_ack = 1;
|
multi_ack = 1;
|
||||||
|
if (strstr(line+45, "thin-pack"))
|
||||||
|
use_thin_pack = 1;
|
||||||
|
|
||||||
/* We have sent all our refs already, and the other end
|
/* We have sent all our refs already, and the other end
|
||||||
* should have chosen out of them; otherwise they are
|
* should have chosen out of them; otherwise they are
|
||||||
|
@ -213,7 +218,7 @@ static int receive_needs(void)
|
||||||
|
|
||||||
static int send_ref(const char *refname, const unsigned char *sha1)
|
static int send_ref(const char *refname, const unsigned char *sha1)
|
||||||
{
|
{
|
||||||
static char *capabilities = "multi_ack";
|
static char *capabilities = "multi_ack thin-pack";
|
||||||
struct object *o = parse_object(sha1);
|
struct object *o = parse_object(sha1);
|
||||||
|
|
||||||
if (!o)
|
if (!o)
|
||||||
|
|
|
@ -111,7 +111,7 @@ int main(int argc, char **argv)
|
||||||
funny = 0;
|
funny = 0;
|
||||||
for (i = 0; i < entries; i++) {
|
for (i = 0; i < entries; i++) {
|
||||||
struct cache_entry *ce = active_cache[i];
|
struct cache_entry *ce = active_cache[i];
|
||||||
if (ntohs(ce->ce_flags) & ~CE_NAMEMASK) {
|
if (ce_stage(ce)) {
|
||||||
if (10 < ++funny) {
|
if (10 < ++funny) {
|
||||||
fprintf(stderr, "...\n");
|
fprintf(stderr, "...\n");
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue