Compare commits
116 Commits
Author | SHA1 | Date | |
---|---|---|---|
60d64db461 | |||
d7bba81575 | |||
08db81a9f1 | |||
7acab8f167 | |||
2db0bfbc04 | |||
b2309b7019 | |||
9add69b1b1 | |||
2ed02887bd | |||
d4072c9722 | |||
b6ebac9e43 | |||
0f3f5e3f69 | |||
1331df8781 | |||
b0c698a6e4 | |||
94d2331770 | |||
6d5410d651 | |||
4b1ca25e42 | |||
0086e2c854 | |||
abacbe4166 | |||
ac0b86dadf | |||
ba1dbb61ea | |||
8614e92323 | |||
54a9ba0d44 | |||
acc075a8ad | |||
ceae78b438 | |||
f7a2eb7359 | |||
7765e7ebda | |||
e6bd23911e | |||
07203659d0 | |||
04e7ca1a1b | |||
d7b1a1ddbe | |||
1c3039e8f1 | |||
9bc0f32c77 | |||
b4ad3552de | |||
5231148202 | |||
2a444781b1 | |||
c283ab21c1 | |||
fd66dbf529 | |||
e433705dd4 | |||
cb93c19365 | |||
f7d24bbefb | |||
6fd72e39af | |||
e9af60c88b | |||
857f26d2f4 | |||
46e651743a | |||
5f3aa197ac | |||
3cab3594e9 | |||
631ba30907 | |||
4bc51db0fe | |||
6b209d4733 | |||
9ce7028531 | |||
53de71f88b | |||
9e5f4a5539 | |||
ed9a540b2b | |||
592ee97d8f | |||
7f10f7c4e4 | |||
0879aa2870 | |||
0867b0125a | |||
f84f9d38eb | |||
0e9ab02da7 | |||
601c978c1b | |||
17cf939724 | |||
ff56fe1ca7 | |||
e3e291fc07 | |||
605607cc62 | |||
66c9ec2555 | |||
ad7db62113 | |||
14df4c4188 | |||
23ea3e201c | |||
a1c292958f | |||
13956670a7 | |||
a489352e3d | |||
68afd5fab7 | |||
ff36de0847 | |||
c44922a781 | |||
186f855fc6 | |||
c8a4f5e542 | |||
e09f5d7b07 | |||
f37d0cc3ff | |||
f4f440a039 | |||
61f81518a2 | |||
23fc63bf8f | |||
cb34882bd6 | |||
f8d294f0a4 | |||
5ca15b8af7 | |||
7d4de59b17 | |||
f3ad062560 | |||
01d4f0e775 | |||
a16db4f472 | |||
109fc2b97b | |||
8168373fe7 | |||
c2c07a5c2a | |||
cbce5d8961 | |||
fcfa32b9e1 | |||
4bfb6b62ff | |||
39b4ac9968 | |||
76bca9d1a9 | |||
f2416c27ef | |||
d0fde471ab | |||
df8171ccb3 | |||
067744bd5d | |||
fb612d54c1 | |||
b2d09f063a | |||
77131db585 | |||
390cb0c17a | |||
a5ae8e64cf | |||
79f6ac77d9 | |||
d1745afa2f | |||
52963a7a3f | |||
92e2eb9c0a | |||
0dd276b871 | |||
5e0306adfa | |||
e66ab03fcf | |||
31b9755a65 | |||
7141b3b780 | |||
44760f1d55 | |||
93d69d8691 |
7
.gitignore
vendored
7
.gitignore
vendored
@ -19,6 +19,7 @@ git-commit
|
||||
git-commit-tree
|
||||
git-convert-objects
|
||||
git-count-objects
|
||||
git-cvsexportcommit
|
||||
git-cvsimport
|
||||
git-daemon
|
||||
git-diff
|
||||
@ -36,10 +37,12 @@ git-get-tar-commit-id
|
||||
git-grep
|
||||
git-hash-object
|
||||
git-http-fetch
|
||||
git-http-push
|
||||
git-index-pack
|
||||
git-init-db
|
||||
git-local-fetch
|
||||
git-log
|
||||
git-lost-found
|
||||
git-ls-files
|
||||
git-ls-remote
|
||||
git-ls-tree
|
||||
@ -58,6 +61,7 @@ git-mktag
|
||||
git-name-rev
|
||||
git-mv
|
||||
git-octopus
|
||||
git-pack-redundant
|
||||
git-pack-objects
|
||||
git-parse-remote
|
||||
git-patch-id
|
||||
@ -70,7 +74,6 @@ git-read-tree
|
||||
git-rebase
|
||||
git-receive-pack
|
||||
git-relink
|
||||
git-rename
|
||||
git-repack
|
||||
git-request-pull
|
||||
git-reset
|
||||
@ -107,6 +110,8 @@ git-verify-tag
|
||||
git-whatchanged
|
||||
git-write-tree
|
||||
git-core-*/?*
|
||||
test-date
|
||||
test-delta
|
||||
*.tar.gz
|
||||
*.dsc
|
||||
*.deb
|
||||
|
1
Documentation/.gitignore
vendored
1
Documentation/.gitignore
vendored
@ -3,3 +3,4 @@
|
||||
*.1
|
||||
*.7
|
||||
howto-index.txt
|
||||
doc.dep
|
||||
|
@ -49,22 +49,22 @@ install: man
|
||||
$(INSTALL) $(DOC_MAN1) $(DESTDIR)/$(man1)
|
||||
$(INSTALL) $(DOC_MAN7) $(DESTDIR)/$(man7)
|
||||
|
||||
# 'include' dependencies
|
||||
$(patsubst %.txt,%.1,$(wildcard git-diff-*.txt)): \
|
||||
diff-format.txt diff-options.txt
|
||||
$(patsubst %.txt,%.html,$(wildcard git-diff-*.txt)): \
|
||||
diff-format.txt diff-options.txt
|
||||
|
||||
$(patsubst %,%.1,git-fetch git-pull git-push): pull-fetch-param.txt
|
||||
$(patsubst %,%.html,git-fetch git-pull git-push): pull-fetch-param.txt
|
||||
#
|
||||
# Determine "include::" file references in asciidoc files.
|
||||
#
|
||||
doc.dep : $(wildcard *.txt) build-docdep.perl
|
||||
rm -f $@+ $@
|
||||
perl ./build-docdep.perl >$@+
|
||||
mv $@+ $@
|
||||
|
||||
$(patsubst %,%.1,git-merge git-pull): merge-pull-opts.txt
|
||||
$(patsubst %,%.html,git-merge git-pull): merge-pull-opts.txt
|
||||
-include doc.dep
|
||||
|
||||
git.7: ../README
|
||||
|
||||
|
||||
clean:
|
||||
rm -f *.xml *.html *.1 *.7 howto-index.txt howto/*.html
|
||||
rm -f *.xml *.html *.1 *.7 howto-index.txt howto/*.html doc.dep
|
||||
|
||||
%.html : %.txt
|
||||
asciidoc -b xhtml11 -d manpage -f asciidoc.conf $<
|
||||
|
50
Documentation/build-docdep.perl
Executable file
50
Documentation/build-docdep.perl
Executable file
@ -0,0 +1,50 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
my %include = ();
|
||||
my %included = ();
|
||||
|
||||
for my $text (<*.txt>) {
|
||||
open I, '<', $text || die "cannot read: $text";
|
||||
while (<I>) {
|
||||
if (/^include::/) {
|
||||
chomp;
|
||||
s/^include::\s*//;
|
||||
s/\[\]//;
|
||||
$include{$text}{$_} = 1;
|
||||
$included{$_} = 1;
|
||||
}
|
||||
}
|
||||
close I;
|
||||
}
|
||||
|
||||
# Do we care about chained includes???
|
||||
my $changed = 1;
|
||||
while ($changed) {
|
||||
$changed = 0;
|
||||
while (my ($text, $included) = each %include) {
|
||||
for my $i (keys %$included) {
|
||||
# $text has include::$i; if $i includes $j
|
||||
# $text indirectly includes $j.
|
||||
if (exists $include{$i}) {
|
||||
for my $j (keys %{$include{$i}}) {
|
||||
if (!exists $include{$text}{$j}) {
|
||||
$include{$text}{$j} = 1;
|
||||
$included{$j} = 1;
|
||||
$changed = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (my ($text, $included) = each %include) {
|
||||
if (! exists $included{$text} &&
|
||||
(my $base = $text) =~ s/\.txt$//) {
|
||||
my ($suffix) = '1';
|
||||
if ($base eq 'git') {
|
||||
$suffix = '7'; # yuck...
|
||||
}
|
||||
print "$base.html $base.$suffix : ", join(" ", keys %$included), "\n";
|
||||
}
|
||||
}
|
@ -8,13 +8,13 @@ git-diff-index <tree-ish>::
|
||||
compares the <tree-ish> and the files on the filesystem.
|
||||
|
||||
git-diff-index --cached <tree-ish>::
|
||||
compares the <tree-ish> and the cache.
|
||||
compares the <tree-ish> and the index.
|
||||
|
||||
git-diff-tree [-r] <tree-ish-1> <tree-ish-2> [<pattern>...]::
|
||||
compares the trees named by the two arguments.
|
||||
|
||||
git-diff-files [<pattern>...]::
|
||||
compares the cache and the files on the filesystem.
|
||||
compares the index and the files on the filesystem.
|
||||
|
||||
|
||||
An output line is formatted this way:
|
||||
@ -47,7 +47,7 @@ That is, from the left to the right:
|
||||
. an LF or a NUL when '-z' option is used, to terminate the record.
|
||||
|
||||
<sha1> is shown as all 0's if a file is new on the filesystem
|
||||
and it is out of sync with the cache.
|
||||
and it is out of sync with the index.
|
||||
|
||||
Example:
|
||||
|
||||
@ -104,7 +104,7 @@ where:
|
||||
The file parameters can point at the user's working file
|
||||
(e.g. `new-file` in "git-diff-files"), `/dev/null` (e.g. `old-file`
|
||||
when a new file is added), or a temporary file (e.g. `old-file` in the
|
||||
cache). 'GIT_EXTERNAL_DIFF' should not worry about unlinking the
|
||||
index). 'GIT_EXTERNAL_DIFF' should not worry about unlinking the
|
||||
temporary file --- it is removed when 'GIT_EXTERNAL_DIFF' exits.
|
||||
|
||||
For a path that is unmerged, 'GIT_EXTERNAL_DIFF' is called with 1
|
||||
|
@ -50,7 +50,7 @@
|
||||
<orderfile>, which has one shell glob pattern per line.
|
||||
|
||||
-R::
|
||||
Swap two inputs; that is, show differences from cache or
|
||||
Swap two inputs; that is, show differences from index or
|
||||
on-disk file to tree contents.
|
||||
|
||||
For more detailed explanation on these common options, see also
|
||||
|
17
Documentation/fetch-options.txt
Normal file
17
Documentation/fetch-options.txt
Normal file
@ -0,0 +1,17 @@
|
||||
-a, \--append::
|
||||
Append ref names and object names of fetched refs to the
|
||||
existing contents of `.git/FETCH_HEAD`. Without this
|
||||
option old data in `.git/FETCH_HEAD` will be overwritten.
|
||||
|
||||
-f, \--force::
|
||||
|
||||
-t, \--tags::
|
||||
By default, the git core utilities will not fetch and store
|
||||
tags under the same name as the remote repository; ask it
|
||||
to do so using `--tags`.
|
||||
|
||||
-u, \--update-head-ok::
|
||||
By default `git-fetch` refuses to update the head which
|
||||
corresponds to the current branch. This flag disables the
|
||||
check. Note that fetching into the current branch will not
|
||||
update the index and working directory, so use it with care.
|
@ -8,7 +8,7 @@ git-apply - Apply patch on a git index file and a work tree
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--index-info] [-z] [<patch>...]
|
||||
'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [-z] [<patch>...]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -72,6 +72,12 @@ OPTIONS
|
||||
patch. Give this flag after those flags to also apply
|
||||
the patch.
|
||||
|
||||
--no-add::
|
||||
When applying a patch, ignore additions made by the
|
||||
patch. This can be used to extract common part between
|
||||
two files by first running `diff` on them and applying
|
||||
the result with this option, which would apply the
|
||||
deletion part but not addition part.
|
||||
|
||||
Author
|
||||
------
|
||||
|
@ -3,11 +3,11 @@ git-branch(1)
|
||||
|
||||
NAME
|
||||
----
|
||||
git-branch - Create a new branch.
|
||||
git-branch - Create a new branch, or remove an old one.
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-branch' [<branchname> [start-point]]
|
||||
'git-branch' [-d | -D] [<branchname> [start-point]]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -19,11 +19,18 @@ created, otherwise it will be created at the current HEAD.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-d::
|
||||
Delete a branch. The branch must be fully merged.
|
||||
|
||||
-D::
|
||||
Delete a branch irrespective of its index status.
|
||||
|
||||
<branchname>::
|
||||
The name of the branch to create.
|
||||
The name of the branch to create or delete.
|
||||
|
||||
start-point::
|
||||
Where to create the branch; defaults to HEAD.
|
||||
Where to create the branch; defaults to HEAD. This
|
||||
option has no meaning with -d and -D.
|
||||
|
||||
Author
|
||||
------
|
||||
|
@ -3,7 +3,7 @@ git-checkout-index(1)
|
||||
|
||||
NAME
|
||||
----
|
||||
git-checkout-index - Copy files from the cache to the working directory
|
||||
git-checkout-index - Copy files from the index to the working directory
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
@ -13,26 +13,26 @@ SYNOPSIS
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Will copy all files listed from the cache to the working directory
|
||||
Will copy all files listed from the index to the working directory
|
||||
(not overwriting existing files).
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-u::
|
||||
-u|--index::
|
||||
update stat information for the checked out entries in
|
||||
the cache file.
|
||||
the index file.
|
||||
|
||||
-q::
|
||||
be quiet if files exist or are not in the cache
|
||||
-q|--quiet::
|
||||
be quiet if files exist or are not in the index
|
||||
|
||||
-f::
|
||||
-f|--force::
|
||||
forces overwrite of existing files
|
||||
|
||||
-a::
|
||||
checks out all files in the cache. Cannot be used
|
||||
-a|--all::
|
||||
checks out all files in the index. Cannot be used
|
||||
together with explicit filenames.
|
||||
|
||||
-n::
|
||||
-n|--no-create::
|
||||
Don't checkout new files, only refresh files already checked
|
||||
out.
|
||||
|
||||
@ -57,7 +57,7 @@ supposed to be able to do things like:
|
||||
|
||||
which will force all existing `*.h` files to be replaced with their
|
||||
cached copies. If an empty command line implied "all", then this would
|
||||
force-refresh everything in the cache, which was not the point.
|
||||
force-refresh everything in the index, which was not the point.
|
||||
|
||||
To update and refresh only the files already checked out:
|
||||
|
||||
@ -74,7 +74,7 @@ desired tree into the index, and do a
|
||||
|
||||
git-checkout-index --prefix=git-export-dir/ -a
|
||||
|
||||
and git-checkout-index will "export" the cache into the specified
|
||||
and git-checkout-index will "export" the index into the specified
|
||||
directory.
|
||||
|
||||
NOTE The final "/" is important. The exported name is literally just
|
||||
|
@ -8,7 +8,7 @@ git-clone - Clones a repository.
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-clone' [-l [-s]] [-q] [-n] [-u <upload-pack>] <repository> <directory>
|
||||
'git-clone' [-l [-s]] [-q] [-n] [-u <upload-pack>] <repository> [<directory>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -68,9 +68,11 @@ OPTIONS
|
||||
be any URL git-fetch supports.
|
||||
|
||||
<directory>::
|
||||
The name of a new directory to be cloned into. It is an
|
||||
error to specify an existing directory.
|
||||
|
||||
The name of a new directory to clone into. The "humanish"
|
||||
part of the source repository is used if no directory is
|
||||
explicitly given ("repo" for "/path/to/repo.git" and "foo"
|
||||
for "host.xz:foo/.git"). Cloning into an existing directory
|
||||
is not allowed.
|
||||
|
||||
Author
|
||||
------
|
||||
@ -78,7 +80,7 @@ Written by Linus Torvalds <torvalds@osdl.org>
|
||||
|
||||
Documentation
|
||||
--------------
|
||||
Documentation by Junio C Hamano.
|
||||
Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
|
||||
|
||||
|
||||
GIT
|
||||
|
@ -8,7 +8,7 @@ git-commit-tree - Creates a new commit object
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-commit-tree' <tree> [-p <parent commit>]\ < changelog
|
||||
'git-commit-tree' <tree> [-p <parent commit>]\* < changelog
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
56
Documentation/git-cvsexportcommit.txt
Normal file
56
Documentation/git-cvsexportcommit.txt
Normal file
@ -0,0 +1,56 @@
|
||||
git-cvsexportcommit(1)
|
||||
======================
|
||||
|
||||
NAME
|
||||
----
|
||||
git-cvsexportcommit - Export a commit to a CVS checkout
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
git-cvsapplycommmit.perl
|
||||
[ -h ] [ -v ] [ -c ] [ -p ] [PARENTCOMMIT] COMMITID
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Exports a commit from GIT to a CVS checkout, making it easier
|
||||
to merge patches from a git repository into a CVS repository.
|
||||
|
||||
Execute it from the root of the CVS working copy. GIT_DIR must be defined.
|
||||
|
||||
It does its best to do the safe thing, it will check that the files are
|
||||
unchanged and up to date in the CVS checkout, and it will not autocommit
|
||||
by default.
|
||||
|
||||
Supports file additions, removals, and commits that affect binary files.
|
||||
|
||||
If the commit is a merge commit, you must tell git-cvsapplycommit what parent
|
||||
should the changeset be done against.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
-c::
|
||||
Commit automatically if the patch applied cleanly. It will not
|
||||
commit if any hunks fail to apply or there were other problems.
|
||||
|
||||
-p::
|
||||
Be pedantic (paranoid) when applying patches. Invokes patch with
|
||||
--fuzz=0
|
||||
|
||||
-v::
|
||||
Verbose.
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Martin Langhoff <martin@catalyst.net.nz>
|
||||
|
||||
Documentation
|
||||
--------------
|
||||
Documentation by Martin Langhoff <martin@catalyst.net.nz>
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the gitlink:git[7] suite
|
||||
|
@ -35,7 +35,7 @@ OPTIONS
|
||||
|
||||
-i::
|
||||
Import-only: don't perform a checkout after importing. This option
|
||||
ensures the working directory and cache remain untouched and will
|
||||
ensures the working directory and index remain untouched and will
|
||||
not create them if they do not exist.
|
||||
|
||||
-k::
|
||||
|
@ -3,7 +3,7 @@ git-diff-files(1)
|
||||
|
||||
NAME
|
||||
----
|
||||
git-diff-files - Compares files in the working tree and the cache
|
||||
git-diff-files - Compares files in the working tree and the index
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
@ -12,9 +12,9 @@ SYNOPSIS
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Compares the files in the working tree and the cache. When paths
|
||||
Compares the files in the working tree and the index. When paths
|
||||
are specified, compares only those named paths. Otherwise all
|
||||
entries in the cache are compared. The output format is the
|
||||
entries in the index are compared. The output format is the
|
||||
same as "git-diff-index" and "git-diff-tree".
|
||||
|
||||
OPTIONS
|
||||
|
@ -3,7 +3,7 @@ git-diff-index(1)
|
||||
|
||||
NAME
|
||||
----
|
||||
git-diff-index - Compares content and mode of blobs between the cache and repository
|
||||
git-diff-index - Compares content and mode of blobs between the index and repository
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
@ -13,10 +13,10 @@ SYNOPSIS
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Compares the content and mode of the blobs found via a tree
|
||||
object with the content of the current cache and, optionally
|
||||
object with the content of the current index and, optionally
|
||||
ignoring the stat state of the file on disk. When paths are
|
||||
specified, compares only those named paths. Otherwise all
|
||||
entries in the cache are compared.
|
||||
entries in the index are compared.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
@ -49,11 +49,11 @@ Cached Mode
|
||||
-----------
|
||||
If '--cached' is specified, it allows you to ask:
|
||||
|
||||
show me the differences between HEAD and the current cache
|
||||
show me the differences between HEAD and the current index
|
||||
contents (the ones I'd write with a "git-write-tree")
|
||||
|
||||
For example, let's say that you have worked on your working directory, updated
|
||||
some files in the cache and are ready to commit. You want to see eactly
|
||||
some files in the index and are ready to commit. You want to see eactly
|
||||
*what* you are going to commit is without having to write a new tree
|
||||
object and compare it that way, and to do that, you just do
|
||||
|
||||
@ -92,7 +92,7 @@ which is obviously a very useful question too, since that tells you what
|
||||
you *could* commit. Again, the output matches the "git-diff-tree -r"
|
||||
output to a tee, but with a twist.
|
||||
|
||||
The twist is that if some file doesn't match the cache, we don't have
|
||||
The twist is that if some file doesn't match the index, we don't have
|
||||
a backing store thing for it, and we use the magic "all-zero" sha1 to
|
||||
show that. So let's say that you have edited `kernel/sched.c`, but
|
||||
have not actually done a "git-update-index" on it yet - there is no
|
||||
@ -110,7 +110,7 @@ NOTE: As with other commands of this type, "git-diff-index" does not
|
||||
actually look at the contents of the file at all. So maybe
|
||||
`kernel/sched.c` hasn't actually changed, and it's just that you
|
||||
touched it. In either case, it's a note that you need to
|
||||
"git-upate-cache" it to make the cache be in sync.
|
||||
"git-upate-index" it to make the index be in sync.
|
||||
|
||||
NOTE: You can have a mixture of files show up as "has been updated"
|
||||
and "is still dirty in the working directory" together. You can always
|
||||
|
@ -8,7 +8,7 @@ git-diff-tree - Compares the content and mode of blobs found via two tree object
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-diff-tree' [--stdin] [-m] [-s] [-v] [--pretty] [-t] [-r] [--root] [<common diff options>] <tree-ish> [<tree-ish>] [<path>...]
|
||||
'git-diff-tree' [--stdin] [-m] [-s] [-v] [--no-commit-id] [--pretty] [-t] [-r] [--root] [<common diff options>] <tree-ish> [<tree-ish>] [<path>...]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -74,6 +74,10 @@ separated with a single space are given.
|
||||
commit message. Without "=<style>", it defaults to
|
||||
medium.
|
||||
|
||||
--no-commit-id::
|
||||
git-diff-tree outputs a line with the commit ID when
|
||||
applicable. This flag suppressed the commit ID output.
|
||||
|
||||
|
||||
Limiting Output
|
||||
---------------
|
||||
|
@ -8,7 +8,7 @@ git-fetch - Download objects and a head from another repository.
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-fetch' <repository> <refspec>...
|
||||
'git-fetch' <options> <repository> <refspec>...
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
@ -17,24 +17,16 @@ Fetches named heads or tags from another repository, along with
|
||||
the objects necessary to complete them.
|
||||
|
||||
The ref names and their object names of fetched refs are stored
|
||||
in $GIT_DIR/FETCH_HEAD. This information is left for a later merge
|
||||
in `.git/FETCH_HEAD`. This information is left for a later merge
|
||||
operation done by "git resolve" or "git octopus".
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
include::fetch-options.txt[]
|
||||
|
||||
include::pull-fetch-param.txt[]
|
||||
|
||||
-a, \--append::
|
||||
Append ref names and object names of fetched refs to the
|
||||
existing contents of $GIT_DIR/FETCH_HEAD. Without this
|
||||
option old data in $GIT_DIR/FETCH_HEAD will be overwritten.
|
||||
|
||||
-u, \--update-head-ok::
|
||||
By default 'git-fetch' refuses to update the head which
|
||||
corresponds to the current branch. This flag disables the
|
||||
check. Note that fetching into the current branch will not
|
||||
update the index and working directory, so use it with care.
|
||||
|
||||
|
||||
SEE ALSO
|
||||
|
@ -33,7 +33,7 @@ index file and all SHA1 references in .git/refs/* as heads.
|
||||
Report tags.
|
||||
|
||||
--cache::
|
||||
Consider any object recorded in the cache also as a head node for
|
||||
Consider any object recorded in the index also as a head node for
|
||||
an unreachability trace.
|
||||
|
||||
--standalone::
|
||||
@ -125,7 +125,7 @@ GIT_OBJECT_DIRECTORY::
|
||||
used to specify the object database root (usually $GIT_DIR/objects)
|
||||
|
||||
GIT_INDEX_FILE::
|
||||
used to specify the index file of the cache
|
||||
used to specify the index file of the index
|
||||
|
||||
GIT_ALTERNATE_OBJECT_DIRECTORIES::
|
||||
used to specify additional object database roots (usually unset)
|
||||
|
@ -16,7 +16,7 @@ Computes the object ID value for an object with specified type
|
||||
with the contents of the named file (which can be outside of the
|
||||
work tree), and optionally writes the resulting object into the
|
||||
object database. Reports its object ID to its standard output.
|
||||
This is used by "git-cvsimport" to update the cache
|
||||
This is used by "git-cvsimport" to update the index
|
||||
without modifying files in the work tree. When <type> is not
|
||||
specified, it defaults to "blob".
|
||||
|
||||
|
78
Documentation/git-lost-found.txt
Normal file
78
Documentation/git-lost-found.txt
Normal file
@ -0,0 +1,78 @@
|
||||
git-lost-found(1)
|
||||
=================
|
||||
|
||||
NAME
|
||||
----
|
||||
git-lost-found - Recover lost refs that luckily have not yet been pruned.
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-lost-found'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Finds dangling commits and tags from the object database, and
|
||||
creates refs to them in .git/lost-found/ directory. Commits and
|
||||
tags that dereference to commits go to .git/lost-found/commit
|
||||
and others are stored in .git/lost-found/other directory.
|
||||
|
||||
|
||||
OUTPUT
|
||||
------
|
||||
One line description from the commit and tag found along with
|
||||
their object name are printed on the standard output.
|
||||
|
||||
|
||||
EXAMPLE
|
||||
-------
|
||||
|
||||
Suppose you run 'git tag -f' and mistyped the tag to overwrite.
|
||||
The ref to your tag is overwritten, but until you run 'git
|
||||
prune', it is still there.
|
||||
|
||||
------------
|
||||
$ git lost-found
|
||||
[1ef2b196d909eed523d4f3c9bf54b78cdd6843c6] GIT 0.99.9c
|
||||
...
|
||||
------------
|
||||
|
||||
Also you can use gitk to browse how they relate to each other
|
||||
and existing (probably old) tags.
|
||||
|
||||
------------
|
||||
$ gitk $(cd .git/lost-found/commit && echo ??*)
|
||||
------------
|
||||
|
||||
After making sure that it is the object you are looking for, you
|
||||
can reconnect it to your regular .git/refs hierarchy.
|
||||
|
||||
------------
|
||||
$ git cat-file -t 1ef2b196
|
||||
tag
|
||||
$ git cat-file tag 1ef2b196
|
||||
object fa41bbce8e38c67a218415de6cfa510c7e50032a
|
||||
type commit
|
||||
tag v0.99.9c
|
||||
tagger Junio C Hamano <junkio@cox.net> 1131059594 -0800
|
||||
|
||||
GIT 0.99.9c
|
||||
|
||||
This contains the following changes from the "master" branch, since
|
||||
...
|
||||
$ git update-ref refs/tags/not-lost-anymore 1ef2b196
|
||||
$ git rev-parse not-lost-anymore
|
||||
1ef2b196d909eed523d4f3c9bf54b78cdd6843c6
|
||||
------------
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Junio C Hamano 濱野 純 <junkio@cox.net>
|
||||
|
||||
Documentation
|
||||
--------------
|
||||
Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
|
||||
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the gitlink:git[7] suite
|
@ -3,7 +3,7 @@ git-ls-files(1)
|
||||
|
||||
NAME
|
||||
----
|
||||
git-ls-files - Information about files in the cache/working directory
|
||||
git-ls-files - Information about files in the index/working directory
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
|
@ -12,7 +12,7 @@ SYNOPSIS
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This looks up the <file>(s) in the cache and, if there are any merge
|
||||
This looks up the <file>(s) in the index and, if there are any merge
|
||||
entries, passes the SHA1 hash for those files as arguments 1, 2, 3 (empty
|
||||
argument if no file), and <file> as argument 4. File modes for the three
|
||||
files are passed as arguments 5, 6 and 7.
|
||||
@ -23,7 +23,7 @@ OPTIONS
|
||||
Interpret all following arguments as filenames.
|
||||
|
||||
-a::
|
||||
Run merge against all files in the cache that need merging.
|
||||
Run merge against all files in the index that need merging.
|
||||
|
||||
-o::
|
||||
Instead of stopping at the first failed merge, do all of them
|
||||
|
@ -19,7 +19,7 @@ which drives multiple merge strategy scripts.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
include::merge-pull-opts.txt[]
|
||||
include::merge-options.txt[]
|
||||
|
||||
<msg>::
|
||||
The commit message to be used for the merge commit (in case
|
||||
|
50
Documentation/git-pack-redundant.txt
Normal file
50
Documentation/git-pack-redundant.txt
Normal file
@ -0,0 +1,50 @@
|
||||
git-pack-redundant(1)
|
||||
=====================
|
||||
|
||||
NAME
|
||||
----
|
||||
git-pack-redundant - Program used to find redundant pack files.
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-pack-redundant [ --verbose ] [ --alt-odb ] < --all | .pack filename ... >'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This program computes which packs in your repository
|
||||
are redundant. The output is suitable for piping to
|
||||
'xargs rm' if you are in the root of the repository.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
|
||||
--all::
|
||||
Processes all packs. Any filenames on the commandline are ignored.
|
||||
|
||||
--alt-odb::
|
||||
Don't require objects present in packs from alternate object
|
||||
directories to be present in local packs.
|
||||
|
||||
--verbose::
|
||||
Outputs some statistics to stderr. Has a small performance penalty.
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Lukas Sandström <lukass@etek.chalmers.se>
|
||||
|
||||
Documentation
|
||||
--------------
|
||||
Documentation by Lukas Sandström <lukass@etek.chalmers.se>
|
||||
|
||||
See-Also
|
||||
--------
|
||||
gitlink:git-pack-objects[1]
|
||||
gitlink:git-repack[1]
|
||||
gitlink:git-prune-packed[1]
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the gitlink:git[7] suite
|
||||
|
@ -23,6 +23,12 @@ compression applied, stored in a single file, with an associated index file.
|
||||
|
||||
Packs are used to reduce the load on mirror systems, backup engines, disk storage, etc.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-n::
|
||||
Don't actually remove any objects, only show those that would have been
|
||||
removed.
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Linus Torvalds <torvalds@osdl.org>
|
||||
|
@ -20,21 +20,18 @@ Note that you can use `.` (current directory) as the
|
||||
<repository> to pull from the local repository -- this is useful
|
||||
when merging local branches into the current branch.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
include::merge-options.txt[]
|
||||
|
||||
include::fetch-options.txt[]
|
||||
|
||||
include::pull-fetch-param.txt[]
|
||||
|
||||
-a, \--append::
|
||||
Append ref names and object names of fetched refs to the
|
||||
existing contents of `$GIT_DIR/FETCH_HEAD`. Without this
|
||||
option old data in `$GIT_DIR/FETCH_HEAD` will be overwritten.
|
||||
|
||||
include::merge-pull-opts.txt[]
|
||||
|
||||
include::merge-strategies.txt[]
|
||||
|
||||
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
|
||||
@ -72,7 +69,7 @@ $ git fetch origin master:origin +pu:pu maint:maint
|
||||
$ git pull . origin
|
||||
------------------------------------------------
|
||||
+
|
||||
Here, a typical `$GIT_DIR/remotes/origin` file from a
|
||||
Here, a typical `.git/remotes/origin` file from a
|
||||
`git-clone` operation is used in combination with
|
||||
command line options to `git-fetch` to first update
|
||||
multiple branches of the local repository and then
|
||||
@ -85,7 +82,7 @@ known to have already obtained and made available
|
||||
all the necessary objects.
|
||||
|
||||
|
||||
Pull of multiple branches from one repository using `$GIT_DIR/remotes` file::
|
||||
Pull of multiple branches from one repository using `.git/remotes` file::
|
||||
+
|
||||
------------------------------------------------
|
||||
$ cat .git/remotes/origin
|
||||
@ -98,7 +95,7 @@ $ git checkout master
|
||||
$ git pull origin
|
||||
------------------------------------------------
|
||||
+
|
||||
Here, a typical `$GIT_DIR/remotes/origin` file from a
|
||||
Here, a typical `.git/remotes/origin` file from a
|
||||
`git-clone` operation has been hand-modified to include
|
||||
the branch-mapping of additional remote and local
|
||||
heads directly. A single `git-pull` operation while
|
||||
|
@ -3,7 +3,7 @@ git-read-tree(1)
|
||||
|
||||
NAME
|
||||
----
|
||||
git-read-tree - Reads tree information into the directory cache
|
||||
git-read-tree - Reads tree information into the index
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
@ -13,11 +13,11 @@ SYNOPSIS
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Reads the tree information given by <tree-ish> into the directory cache,
|
||||
Reads the tree information given by <tree-ish> into the index,
|
||||
but does not actually *update* any of the files it "caches". (see:
|
||||
git-checkout-index)
|
||||
|
||||
Optionally, it can merge a tree into the cache, perform a
|
||||
Optionally, it can merge a tree into the index, perform a
|
||||
fast-forward (i.e. 2-way) merge, or a 3-way merge, with the -m
|
||||
flag. When used with -m, the -u flag causes it to also update
|
||||
the files in the work tree with the result of the merge.
|
||||
@ -30,6 +30,10 @@ OPTIONS
|
||||
-m::
|
||||
Perform a merge, not just a read.
|
||||
|
||||
--reset::
|
||||
|
||||
Same as -m except that unmerged entries will be silently ignored.
|
||||
|
||||
-u::
|
||||
After a successful merge, update the files in the work
|
||||
tree with the result of the merge.
|
||||
@ -59,10 +63,10 @@ provided.
|
||||
Single Tree Merge
|
||||
~~~~~~~~~~~~~~~~~
|
||||
If only 1 tree is specified, git-read-tree operates as if the user did not
|
||||
specify '-m', except that if the original cache has an entry for a
|
||||
specify '-m', except that if the original index has an entry for a
|
||||
given pathname, and the contents of the path matches with the tree
|
||||
being read, the stat info from the cache is used. (In other words, the
|
||||
cache's stat()s take precedence over the merged tree's).
|
||||
being read, the stat info from the index is used. (In other words, the
|
||||
index's stat()s take precedence over the merged tree's).
|
||||
|
||||
That means that if you do a "git-read-tree -m <newtree>" followed by a
|
||||
"git-checkout-index -f -u -a", the "git-checkout-index" only checks out
|
||||
@ -96,7 +100,7 @@ Here are the "carry forward" rules:
|
||||
-------------------------------------------------------
|
||||
0 nothing nothing nothing (does not happen)
|
||||
1 nothing nothing exists use M
|
||||
2 nothing exists nothing remove path from cache
|
||||
2 nothing exists nothing remove path from index
|
||||
3 nothing exists exists use M
|
||||
|
||||
clean I==H I==M
|
||||
@ -109,7 +113,7 @@ Here are the "carry forward" rules:
|
||||
8 yes N/A no nothing exists fail
|
||||
9 no N/A no nothing exists fail
|
||||
|
||||
10 yes yes N/A exists nothing remove path from cache
|
||||
10 yes yes N/A exists nothing remove path from index
|
||||
11 no yes N/A exists nothing fail
|
||||
12 yes no N/A exists nothing fail
|
||||
13 no no N/A exists nothing fail
|
||||
@ -128,7 +132,7 @@ Here are the "carry forward" rules:
|
||||
20 yes yes no exists exists use M
|
||||
21 no yes no exists exists fail
|
||||
|
||||
In all "keep index" cases, the cache entry stays as in the
|
||||
In all "keep index" cases, the index entry stays as in the
|
||||
original index file. If the entry were not up to date,
|
||||
git-read-tree keeps the copy in the work tree intact when
|
||||
operating under the -u flag.
|
||||
@ -245,7 +249,7 @@ since you pulled from him:
|
||||
|
||||
Your work tree is still based on your HEAD ($JC), but you have
|
||||
some edits since. Three-way merge makes sure that you have not
|
||||
added or modified cache entries since $JC, and if you haven't,
|
||||
added or modified index entries since $JC, and if you haven't,
|
||||
then does the right thing. So with the following sequence:
|
||||
|
||||
$ git-read-tree -m -u `git-merge-base $JC $LT` $JC $LT
|
||||
|
@ -1,32 +0,0 @@
|
||||
git-rename(1)
|
||||
=============
|
||||
|
||||
NAME
|
||||
----
|
||||
git-rename - Script used to rename a file, directory or symlink.
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-rename' <source> <destination>
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This script is used to rename a file, directory or symlink.
|
||||
|
||||
The index is updated after successful completion, but the change must still be
|
||||
committed.
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Linus Torvalds <torvalds@osdl.org>
|
||||
Rewritten by Ryan Anderson <ryan@michonline.com>
|
||||
|
||||
Documentation
|
||||
--------------
|
||||
Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the gitlink:git[7] suite
|
||||
|
@ -44,7 +44,7 @@ When importing incementally, you might need to edit the .git/svn2git file.
|
||||
|
||||
-i::
|
||||
Import-only: don't perform a checkout after importing. This option
|
||||
ensures the working directory and cache remain untouched and will
|
||||
ensures the working directory and index remain untouched and will
|
||||
not create them if they do not exist.
|
||||
|
||||
-t <trunk_subdir>::
|
||||
|
@ -8,7 +8,7 @@ git-tag - Create a tag object signed with GPG
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-tag' [-a | -s | -u <key-id>] [-f] [-m <msg>] <name> [<head>]
|
||||
'git-tag' [-a | -s | -u <key-id>] [-f | -d] [-m <msg>] <name> [<head>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -30,6 +30,8 @@ A GnuPG signed tag object will be created when `-s` or `-u
|
||||
committer identity for the current user is used to find the
|
||||
GnuPG key for signing.
|
||||
|
||||
`-d <tag>` deletes the tag.
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
|
@ -8,7 +8,7 @@ git-unpack-objects - Unpack objects from a packed archive.
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-unpack-objects' [-q] <pack-file
|
||||
'git-unpack-objects' [-n] [-q] <pack-file
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
@ -19,6 +19,10 @@ one-object" format in $GIT_OBJECT_DIRECTORY.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-n::
|
||||
Only list the objects that would be unpacked, don't actually unpack
|
||||
them.
|
||||
|
||||
-q::
|
||||
The command usually shows percentage progress. This
|
||||
flag suppresses it.
|
||||
|
@ -21,7 +21,7 @@ SYNOPSIS
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Modifies the index or directory cache. Each file mentioned is updated
|
||||
into the cache and any 'unmerged' or 'needs updating' state is
|
||||
into the index and any 'unmerged' or 'needs updating' state is
|
||||
cleared.
|
||||
|
||||
The way "git-update-index" handles files it is told about can be modified
|
||||
@ -30,26 +30,26 @@ using the various options:
|
||||
OPTIONS
|
||||
-------
|
||||
--add::
|
||||
If a specified file isn't in the cache already then it's
|
||||
If a specified file isn't in the index already then it's
|
||||
added.
|
||||
Default behaviour is to ignore new files.
|
||||
|
||||
--remove::
|
||||
If a specified file is in the cache but is missing then it's
|
||||
If a specified file is in the index but is missing then it's
|
||||
removed.
|
||||
Default behaviour is to ignore removed file.
|
||||
|
||||
--refresh::
|
||||
Looks at the current cache and checks to see if merges or
|
||||
Looks at the current index and checks to see if merges or
|
||||
updates are needed by checking stat() information.
|
||||
|
||||
-q::
|
||||
Quiet. If --refresh finds that the cache needs an update, the
|
||||
Quiet. If --refresh finds that the index needs an update, the
|
||||
default behavior is to error out. This option makes
|
||||
git-update-index continue anyway.
|
||||
|
||||
--unmerged::
|
||||
If --refresh finds unmerged changes in the cache, the default
|
||||
If --refresh finds unmerged changes in the index, the default
|
||||
behavior is to error out. This option makes git-update-index
|
||||
continue anyway.
|
||||
|
||||
@ -57,10 +57,10 @@ OPTIONS
|
||||
Ignores missing files during a --refresh
|
||||
|
||||
--cacheinfo <mode> <object> <path>::
|
||||
Directly insert the specified info into the cache.
|
||||
Directly insert the specified info into the index.
|
||||
|
||||
--index-info::
|
||||
Read index info from stdin.
|
||||
Read index information from stdin.
|
||||
|
||||
--chmod=(+|-)x::
|
||||
Set the execute permissions on the updated files.
|
||||
@ -68,7 +68,7 @@ OPTIONS
|
||||
--info-only::
|
||||
Do not create objects in the object database for all
|
||||
<file> arguments that follow this flag; just insert
|
||||
their object IDs into the cache.
|
||||
their object IDs into the index.
|
||||
|
||||
--force-remove::
|
||||
Remove the file from the index even when the working directory
|
||||
@ -106,14 +106,14 @@ OPTIONS
|
||||
|
||||
Using --refresh
|
||||
---------------
|
||||
'--refresh' does not calculate a new sha1 file or bring the cache
|
||||
'--refresh' does not calculate a new sha1 file or bring the index
|
||||
up-to-date for mode/content changes. But what it *does* do is to
|
||||
"re-match" the stat information of a file with the cache, so that you
|
||||
can refresh the cache for a file that hasn't been changed but where
|
||||
"re-match" the stat information of a file with the index, so that you
|
||||
can refresh the index for a file that hasn't been changed but where
|
||||
the stat entry is out of date.
|
||||
|
||||
For example, you'd want to do this after doing a "git-read-tree", to link
|
||||
up the stat cache details with the proper files.
|
||||
up the stat index details with the proper files.
|
||||
|
||||
Using --cacheinfo or --info-only
|
||||
--------------------------------
|
||||
|
@ -3,7 +3,7 @@ git-write-tree(1)
|
||||
|
||||
NAME
|
||||
----
|
||||
git-write-tree - Creates a tree object from the current cache
|
||||
git-write-tree - Creates a tree object from the current index
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
@ -12,11 +12,11 @@ SYNOPSIS
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Creates a tree object using the current cache.
|
||||
Creates a tree object using the current index.
|
||||
|
||||
The cache must be merged.
|
||||
The index must be merged.
|
||||
|
||||
Conceptually, "git-write-tree" sync()s the current directory cache contents
|
||||
Conceptually, "git-write-tree" sync()s the current index contents
|
||||
into a set of tree files.
|
||||
In order to have that match what is actually in your directory right
|
||||
now, you need to have done a "git-update-index" phase before you did the
|
||||
|
@ -40,7 +40,7 @@ reflect recent changes.
|
||||
Commands Overview
|
||||
-----------------
|
||||
The git commands can helpfully be split into those that manipulate
|
||||
the repository, the cache and the working fileset, those that
|
||||
the repository, the index and the working fileset, those that
|
||||
interrogate and compare them, and those that moves objects and
|
||||
references between repositories.
|
||||
|
||||
@ -59,7 +59,7 @@ gitlink:git-apply[1]::
|
||||
applies it to the working tree.
|
||||
|
||||
gitlink:git-checkout-index[1]::
|
||||
Copy files from the cache to the working directory
|
||||
Copy files from the index to the working directory
|
||||
|
||||
gitlink:git-commit-tree[1]::
|
||||
Creates a new commit object
|
||||
@ -86,7 +86,7 @@ gitlink:git-prune-packed[1]::
|
||||
Remove extra objects that are already in pack files.
|
||||
|
||||
gitlink:git-read-tree[1]::
|
||||
Reads tree information into the directory cache
|
||||
Reads tree information into the directory index
|
||||
|
||||
gitlink:git-unpack-objects[1]::
|
||||
Unpacks objects out of a packed archive.
|
||||
@ -95,7 +95,7 @@ gitlink:git-update-index[1]::
|
||||
Modifies the index or directory cache
|
||||
|
||||
gitlink:git-write-tree[1]::
|
||||
Creates a tree from the current cache
|
||||
Creates a tree from the current index
|
||||
|
||||
|
||||
Interrogation commands
|
||||
@ -105,10 +105,10 @@ gitlink:git-cat-file[1]::
|
||||
Provide content or type information for repository objects
|
||||
|
||||
gitlink:git-diff-index[1]::
|
||||
Compares content and mode of blobs between the cache and repository
|
||||
Compares content and mode of blobs between the index and repository
|
||||
|
||||
gitlink:git-diff-files[1]::
|
||||
Compares files in the working tree and the cache
|
||||
Compares files in the working tree and the index
|
||||
|
||||
gitlink:git-diff-stages[1]::
|
||||
Compares two "merge stages" in the index file.
|
||||
@ -120,7 +120,7 @@ gitlink:git-fsck-objects[1]::
|
||||
Verifies the connectivity and validity of the objects in the database
|
||||
|
||||
gitlink:git-ls-files[1]::
|
||||
Information about files in the cache/working directory
|
||||
Information about files in the index/working directory
|
||||
|
||||
gitlink:git-ls-tree[1]::
|
||||
Displays a tree object in human readable form
|
||||
@ -262,9 +262,6 @@ gitlink:git-push[1]::
|
||||
gitlink:git-rebase[1]::
|
||||
Rebase local commits to new upstream head.
|
||||
|
||||
gitlink:git-rename[1]::
|
||||
Rename files and directories.
|
||||
|
||||
gitlink:git-repack[1]::
|
||||
Pack unpacked objects in a repository.
|
||||
|
||||
@ -309,6 +306,9 @@ gitlink:git-convert-objects[1]::
|
||||
gitlink:git-cvsimport[1]::
|
||||
Salvage your data out of another SCM people love to hate.
|
||||
|
||||
gitlink:git-lost-found[1]::
|
||||
Recover lost refs that luckily have not yet been pruned.
|
||||
|
||||
gitlink:git-merge-one-file[1]::
|
||||
The standard helper program to use with "git-merge-index"
|
||||
|
||||
@ -490,8 +490,8 @@ git so take care if using Cogito etc
|
||||
|
||||
'GIT_INDEX_FILE'::
|
||||
This environment allows the specification of an alternate
|
||||
cache/index file. If not specified, the default of
|
||||
`$GIT_DIR/index` is used.
|
||||
index file. If not specified, the default of `$GIT_DIR/index`
|
||||
is used.
|
||||
|
||||
'GIT_OBJECT_DIRECTORY'::
|
||||
If the object storage directory is specified via this
|
||||
|
@ -43,14 +43,14 @@ DAG::
|
||||
|
||||
index::
|
||||
A collection of files with stat information, whose contents are
|
||||
stored as objects. The cache is a stored version of your working
|
||||
stored as objects. The index is a stored version of your working
|
||||
tree. Truth be told, it can also contain a second, and even a third
|
||||
version of a working tree, which are used when merging.
|
||||
|
||||
index entry::
|
||||
The information regarding a particular file, stored in the index.
|
||||
An index entry can be unmerged, if a merge was started, but not
|
||||
yet finished (i.e. if the cache contains multiple versions of
|
||||
yet finished (i.e. if the index contains multiple versions of
|
||||
that file).
|
||||
|
||||
unmerged index:
|
||||
@ -75,7 +75,7 @@ checkout::
|
||||
stored in the object database.
|
||||
|
||||
commit::
|
||||
As a verb: The action of storing the current state of the cache in the
|
||||
As a verb: The action of storing the current state of the index in the
|
||||
object database. The result is a revision.
|
||||
As a noun: Short hand for commit object.
|
||||
|
||||
|
65
Documentation/howto/isolate-bugs-with-bisect.txt
Normal file
65
Documentation/howto/isolate-bugs-with-bisect.txt
Normal file
@ -0,0 +1,65 @@
|
||||
From: Linus Torvalds <torvalds () osdl ! org>
|
||||
To: git@vger.kernel.org
|
||||
Date: 2005-11-08 1:31:34
|
||||
Subject: Real-life kernel debugging scenario
|
||||
Abstract: Short-n-sweet, Linus tells us how to leverage `git-bisect` to perform
|
||||
bug isolation on a repository where "good" and "bad" revisions are known
|
||||
in order to identify a suspect commit.
|
||||
|
||||
|
||||
How To Use git-bisect To Isolate a Bogus Commit
|
||||
===============================================
|
||||
|
||||
The way to use "git bisect" couldn't be easier.
|
||||
|
||||
Figure out what the oldest bad state you know about is (that's usually the
|
||||
head of "master", since that's what you just tried to boot and failed at).
|
||||
Also, figure out the most recent known-good commit (usually the _previous_
|
||||
kernel you ran: and if you've only done a single "pull" in between, it
|
||||
will be ORIG_HEAD).
|
||||
|
||||
Then do
|
||||
|
||||
git bisect start
|
||||
git bisect bad master <- mark "master" as the bad state
|
||||
git bisect good ORIG_HEAD <- mark ORIG_HEAD as good (or
|
||||
whatever other known-good
|
||||
thing you booted laste)
|
||||
|
||||
and at this point "git bisect" will churn for a while, and tell you what
|
||||
the mid-point between those two commits are, and check that state out as
|
||||
the head of the bew "bisect" branch.
|
||||
|
||||
Compile and reboot.
|
||||
|
||||
If it's good, just do
|
||||
|
||||
git bisect good <- mark current head as good
|
||||
|
||||
otherwise, reboot into a good kernel instead, and do (surprise surprise,
|
||||
git really is very intuitive):
|
||||
|
||||
git bisect bad <- mark current head as bad
|
||||
|
||||
and whatever you do, git will select a new half-way point. Do this for a
|
||||
while, until git tells you exactly which commit was the first bad commit.
|
||||
That's your culprit.
|
||||
|
||||
It really works wonderfully well, except for the case where there was
|
||||
_another_ commit that broke something in between, like introduced some
|
||||
stupid compile error. In that case you should not mark that commit good or
|
||||
bad: you should try to find another commit close-by, and do a "git reset
|
||||
--hard <newcommit>" to try out _that_ commit instead, and then test that
|
||||
instead (and mark it good or bad).
|
||||
|
||||
You can do "git bisect visualize" while you do all this to see what's
|
||||
going on by starting up gitk on the bisection range.
|
||||
|
||||
Finally, once you've figured out exactly which commit was bad, you can
|
||||
then go back to the master branch, and try reverting just that commit:
|
||||
|
||||
git checkout master
|
||||
git revert <bad-commit-id>
|
||||
|
||||
to verify that the top-of-kernel works with that single commit reverted.
|
||||
|
@ -63,7 +63,7 @@ And then, you can just remove the broken branch if you decide you really
|
||||
don't want it:
|
||||
|
||||
# remove 'broken' branch
|
||||
rm .git/refs/heads/broken
|
||||
git branch -d broken
|
||||
|
||||
# Prune old objects if you're really really sure
|
||||
git prune
|
||||
|
@ -153,7 +153,8 @@ Everything is in the good order. I do not need the temporary branch
|
||||
nor tag anymore, so remove them:
|
||||
|
||||
------------------------------------------------
|
||||
$ rm -f .git/refs/tags/pu-anchor .git/refs/heads/revert-c99
|
||||
$ rm -f .git/refs/tags/pu-anchor
|
||||
$ git branch -d revert-c99
|
||||
------------------------------------------------
|
||||
|
||||
It was an emergency fix, so we might as well merge it into the
|
||||
|
@ -9,7 +9,7 @@ GIT as a Linux subsystem maintainer.
|
||||
|
||||
-Tony
|
||||
|
||||
Last updated w.r.t. GIT 0.99.5
|
||||
Last updated w.r.t. GIT 0.99.9f
|
||||
|
||||
Linux subsystem maintenance using GIT
|
||||
-------------------------------------
|
||||
@ -89,8 +89,8 @@ out at the current tip of the linus branch.
|
||||
|
||||
These can be easily kept up to date by merging from the "linus" branch:
|
||||
|
||||
$ git checkout test && git resolve test linus "Auto-update from upstream"
|
||||
$ git checkout release && git resolve release linus "Auto-update from upstream"
|
||||
$ git checkout test && git merge "Auto-update from upstream" test linus
|
||||
$ git checkout release && git merge "Auto-update from upstream" release linus
|
||||
|
||||
Set up so that you can push upstream to your public tree (you need to
|
||||
log-in to the remote system and create an empty tree there before the
|
||||
@ -128,7 +128,7 @@ commit to this branch.
|
||||
When you are happy with the state of this change, you can pull it into the
|
||||
"test" branch in preparation to make it public:
|
||||
|
||||
$ git checkout test && git resolve test speed-up-spinlocks "Pull speed-up-spinlock changes"
|
||||
$ git checkout test && git merge "Pull speed-up-spinlock changes" test speed-up-spinlocks
|
||||
|
||||
It is unlikely that you would have any conflicts here ... but you might if you
|
||||
spent a while on this step and had also pulled new versions from upstream.
|
||||
@ -138,7 +138,7 @@ same branch into the "release" tree ready to go upstream. This is where you
|
||||
see the value of keeping each patch (or patch series) in its own branch. It
|
||||
means that the patches can be moved into the "release" tree in any order.
|
||||
|
||||
$ git checkout release && git resolve release speed-up-spinlocks "Pull speed-up-spinlock changes"
|
||||
$ git checkout release && git merge "Pull speed-up-spinlock changes" release speed-up-spinlocks
|
||||
|
||||
After a while, you will have a number of branches, and despite the
|
||||
well chosen names you picked for each of them, you may forget what
|
||||
@ -166,7 +166,7 @@ output from:
|
||||
|
||||
is empty. At this point the branch can be deleted:
|
||||
|
||||
$ rm .git/refs/heads/branchname
|
||||
$ git branch -d branchname
|
||||
|
||||
Some changes are so trivial that it is not necessary to create a separate
|
||||
branch and then merge into each of the test and release branches. For
|
||||
@ -190,7 +190,7 @@ Here are some of the scripts that I use to simplify all this even further.
|
||||
|
||||
case "$1" in
|
||||
test|release)
|
||||
git checkout $1 && git resolve $1 linus "Auto-update from upstream"
|
||||
git checkout $1 && git merge "Auto-update from upstream" $1 linus
|
||||
;;
|
||||
linus)
|
||||
before=$(cat .git/refs/heads/linus)
|
||||
@ -231,7 +231,7 @@ test|release)
|
||||
echo $1 already merged into $2 1>&2
|
||||
exit 1
|
||||
fi
|
||||
git checkout $2 && git resolve $2 $1 "Pull $1 into $2 branch"
|
||||
git checkout $2 && git merge "Pull $1 into $2 branch" $2 $1
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
|
16
Documentation/merge-options.txt
Normal file
16
Documentation/merge-options.txt
Normal file
@ -0,0 +1,16 @@
|
||||
-n, \--no-summary::
|
||||
Do not show diffstat at the end of the merge.
|
||||
|
||||
--no-commit::
|
||||
Perform the merge but pretend the merge failed and do
|
||||
not autocommit, to give the user a chance to inspect and
|
||||
further tweak the merge result before committing.
|
||||
|
||||
|
||||
-s <strategy>, \--strategy=<strategy>::
|
||||
Use the given merge strategy; can be supplied more than
|
||||
once to specify them in the order they should be tried.
|
||||
If there is no `-s` option, a built-in list of strategies
|
||||
is used instead (`git-merge-resolve` when merging a single
|
||||
head, `git-merge-octopus` otherwise).
|
||||
|
@ -1,14 +0,0 @@
|
||||
-n, --no-summary::
|
||||
Do not show diffstat at the end of the merge.
|
||||
|
||||
--no-commit::
|
||||
Perform the merge but pretend the merge failed and do
|
||||
not autocommit, to give the user a chance to inspect and
|
||||
further tweak the merge result before committing.
|
||||
|
||||
-s <strategy>::
|
||||
use that merge strategy; can be given more than once to
|
||||
specify them in the order they should be tried. If
|
||||
there is no `-s` option, built-in list of strategies is
|
||||
used instead (`git-merge-resolve` when merging a single
|
||||
head, `git-merge-octopus` otherwise).
|
@ -8,7 +8,7 @@
|
||||
- Rsync URL: rsync://remote.machine/path/to/repo.git/
|
||||
- HTTP(s) URL: http://remote.machine/path/to/repo.git/
|
||||
- git URL: git://remote.machine/path/to/repo.git/
|
||||
or remote.machine:/path/to/repo.git/
|
||||
- ssh URL: remote.machine:/path/to/repo.git/
|
||||
- Local directory: /path/to/repo.git/
|
||||
===============================================================
|
||||
+
|
||||
|
@ -36,14 +36,16 @@ To start up, create a subdirectory for it, change into that
|
||||
subdirectory, and initialize the git infrastructure with `git-init-db`:
|
||||
|
||||
------------------------------------------------
|
||||
mkdir git-tutorial
|
||||
cd git-tutorial
|
||||
git-init-db
|
||||
$ mkdir git-tutorial
|
||||
$ cd git-tutorial
|
||||
$ git-init-db
|
||||
------------------------------------------------
|
||||
|
||||
to which git will reply
|
||||
|
||||
defaulting to local storage area
|
||||
----------------
|
||||
defaulting to local storage area
|
||||
----------------
|
||||
|
||||
which is just git's way of saying that you haven't been doing anything
|
||||
strange, and that it will have created a local `.git` directory setup for
|
||||
@ -114,8 +116,8 @@ in your git repository. We'll start off with a few bad examples, just to
|
||||
get a feel for how this works:
|
||||
|
||||
------------------------------------------------
|
||||
echo "Hello World" >hello
|
||||
echo "Silly example" >example
|
||||
$ echo "Hello World" >hello
|
||||
$ echo "Silly example" >example
|
||||
------------------------------------------------
|
||||
|
||||
you have now created two files in your working tree (aka 'working directory'), but to
|
||||
@ -129,7 +131,7 @@ actually check in your hard work, you will have to go through two steps:
|
||||
The first step is trivial: when you want to tell git about any changes
|
||||
to your working tree, you use the `git-update-index` program. That
|
||||
program normally just takes a list of filenames you want to update, but
|
||||
to avoid trivial mistakes, it refuses to add new entries to the cache
|
||||
to avoid trivial mistakes, it refuses to add new entries to the index
|
||||
(or remove existing ones) unless you explicitly tell it that you're
|
||||
adding a new entry with the `\--add` flag (or removing an entry with the
|
||||
`\--remove`) flag.
|
||||
@ -137,7 +139,7 @@ adding a new entry with the `\--add` flag (or removing an entry with the
|
||||
So to populate the index with the two files you just created, you can do
|
||||
|
||||
------------------------------------------------
|
||||
git-update-index --add hello example
|
||||
$ git-update-index --add hello example
|
||||
------------------------------------------------
|
||||
|
||||
and you have now told git to track those two files.
|
||||
@ -146,12 +148,17 @@ In fact, as you did that, if you now look into your object directory,
|
||||
you'll notice that git will have added two new objects to the object
|
||||
database. If you did exactly the steps above, you should now be able to do
|
||||
|
||||
ls .git/objects/??/*
|
||||
|
||||
----------------
|
||||
$ ls .git/objects/??/*
|
||||
----------------
|
||||
|
||||
and see two files:
|
||||
|
||||
.git/objects/55/7db03de997c86a4a028e1ebd3a1ceb225be238
|
||||
.git/objects/f2/4c74a2e500f5ee1332c86b94199f52b1d1d962
|
||||
----------------
|
||||
.git/objects/55/7db03de997c86a4a028e1ebd3a1ceb225be238
|
||||
.git/objects/f2/4c74a2e500f5ee1332c86b94199f52b1d1d962
|
||||
----------------
|
||||
|
||||
which correspond with the objects with names of 557db... and f24c7..
|
||||
respectively.
|
||||
@ -159,13 +166,17 @@ respectively.
|
||||
If you want to, you can use `git-cat-file` to look at those objects, but
|
||||
you'll have to use the object name, not the filename of the object:
|
||||
|
||||
git-cat-file -t 557db03de997c86a4a028e1ebd3a1ceb225be238
|
||||
----------------
|
||||
$ git-cat-file -t 557db03de997c86a4a028e1ebd3a1ceb225be238
|
||||
----------------
|
||||
|
||||
where the `-t` tells `git-cat-file` to tell you what the "type" of the
|
||||
object is. git will tell you that you have a "blob" object (ie just a
|
||||
regular file), and you can see the contents with
|
||||
|
||||
git-cat-file "blob" 557db03
|
||||
----------------
|
||||
$ git-cat-file "blob" 557db03
|
||||
----------------
|
||||
|
||||
which will print out "Hello World". The object 557db03 is nothing
|
||||
more than the contents of your file `hello`.
|
||||
@ -188,7 +199,7 @@ was just to show that `git-update-index` did something magical, and
|
||||
actually saved away the contents of your files into the git object
|
||||
database.
|
||||
|
||||
Updating the cache did something else too: it created a `.git/index`
|
||||
Updating the index did something else too: it created a `.git/index`
|
||||
file. This is the index that describes your current working tree, and
|
||||
something you should be very aware of. Again, you normally never worry
|
||||
about the index file itself, but you should be aware of the fact that
|
||||
@ -202,7 +213,7 @@ In particular, let's not even check in the two files into git yet, we'll
|
||||
start off by adding another line to `hello` first:
|
||||
|
||||
------------------------------------------------
|
||||
echo "It's a new day for git" >>hello
|
||||
$ echo "It's a new day for git" >>hello
|
||||
------------------------------------------------
|
||||
|
||||
and you can now, since you told git about the previous state of `hello`, ask
|
||||
@ -210,7 +221,7 @@ git what has changed in the tree compared to your old index, using the
|
||||
`git-diff-files` command:
|
||||
|
||||
------------
|
||||
git-diff-files
|
||||
$ git-diff-files
|
||||
------------
|
||||
|
||||
Oops. That wasn't very readable. It just spit out its own internal
|
||||
@ -222,12 +233,7 @@ To make it readable, we can tell git-diff-files to output the
|
||||
differences as a patch, using the `-p` flag:
|
||||
|
||||
------------
|
||||
git-diff-files -p
|
||||
------------
|
||||
|
||||
which will spit out
|
||||
|
||||
------------
|
||||
$ git-diff-files -p
|
||||
diff --git a/hello b/hello
|
||||
index 557db03..263414f 100644
|
||||
--- a/hello
|
||||
@ -264,13 +270,15 @@ filenames with their contents (and their permissions), and we're
|
||||
creating the equivalent of a git "directory" object:
|
||||
|
||||
------------------------------------------------
|
||||
git-write-tree
|
||||
$ git-write-tree
|
||||
------------------------------------------------
|
||||
|
||||
and this will just output the name of the resulting tree, in this case
|
||||
(if you have done exactly as I've described) it should be
|
||||
|
||||
8988da15d077d4829fc51d8544c097def6644dbb
|
||||
----------------
|
||||
8988da15d077d4829fc51d8544c097def6644dbb
|
||||
----------------
|
||||
|
||||
which is another incomprehensible object name. Again, if you want to,
|
||||
you can use `git-cat-file -t 8988d\...` to see that this time the object
|
||||
@ -299,14 +307,16 @@ that's exactly what `git-commit-tree` spits out, we can do this
|
||||
all with a sequence of simple shell commands:
|
||||
|
||||
------------------------------------------------
|
||||
tree=$(git-write-tree)
|
||||
commit=$(echo 'Initial commit' | git-commit-tree $tree)
|
||||
git-update-ref HEAD $(commit)
|
||||
$ tree=$(git-write-tree)
|
||||
$ commit=$(echo 'Initial commit' | git-commit-tree $tree)
|
||||
$ git-update-ref HEAD $commit
|
||||
------------------------------------------------
|
||||
|
||||
which will say:
|
||||
|
||||
Committing initial tree 8988da15d077d4829fc51d8544c097def6644dbb
|
||||
----------------
|
||||
Committing initial tree 8988da15d077d4829fc51d8544c097def6644dbb
|
||||
----------------
|
||||
|
||||
just to warn you about the fact that it created a totally new commit
|
||||
that is not related to anything else. Normally you do this only *once*
|
||||
@ -349,7 +359,9 @@ didn't have anything to diff against.
|
||||
|
||||
But now we can do
|
||||
|
||||
git-diff-index -p HEAD
|
||||
----------------
|
||||
$ git-diff-index -p HEAD
|
||||
----------------
|
||||
|
||||
(where `-p` has the same meaning as it did in `git-diff-files`), and it
|
||||
will show us the same difference, but for a totally different reason.
|
||||
@ -360,7 +372,9 @@ are obviously the same, so we get the same result.
|
||||
Again, because this is a common operation, you can also just shorthand
|
||||
it with
|
||||
|
||||
git diff HEAD
|
||||
----------------
|
||||
$ git diff HEAD
|
||||
----------------
|
||||
|
||||
which ends up doing the above for you.
|
||||
|
||||
@ -396,7 +410,7 @@ work through the index file, so the first thing we need to do is to
|
||||
update the index cache:
|
||||
|
||||
------------------------------------------------
|
||||
git-update-index hello
|
||||
$ git-update-index hello
|
||||
------------------------------------------------
|
||||
|
||||
(note how we didn't need the `\--add` flag this time, since git knew
|
||||
@ -417,7 +431,7 @@ this wasn't an initial commit any more), but you've done that once
|
||||
already, so let's just use the helpful script this time:
|
||||
|
||||
------------------------------------------------
|
||||
git commit
|
||||
$ git commit
|
||||
------------------------------------------------
|
||||
|
||||
which starts an editor for you to write the commit message and tells you
|
||||
@ -426,7 +440,7 @@ a bit about what you have done.
|
||||
Write whatever message you want, and all the lines that start with '#'
|
||||
will be pruned out, and the rest will be used as the commit message for
|
||||
the change. If you decide you don't want to commit anything after all at
|
||||
this point (you can continue to edit things and update the cache), you
|
||||
this point (you can continue to edit things and update the index), you
|
||||
can just leave an empty message. Otherwise `git commit` will commit
|
||||
the change for you.
|
||||
|
||||
@ -450,7 +464,9 @@ give it just a single commit object, and it will figure out the parent
|
||||
of that commit itself, and show the difference directly. Thus, to get
|
||||
the same diff that we've already seen several times, we can now do
|
||||
|
||||
git-diff-tree -p HEAD
|
||||
----------------
|
||||
$ git-diff-tree -p HEAD
|
||||
----------------
|
||||
|
||||
(again, `-p` means to show the difference as a human-readable patch),
|
||||
and it will show what the last commit (in `HEAD`) actually changed.
|
||||
@ -505,13 +521,17 @@ activities.
|
||||
To see the whole history of our pitiful little git-tutorial project, you
|
||||
can do
|
||||
|
||||
git log
|
||||
----------------
|
||||
$ git log
|
||||
----------------
|
||||
|
||||
which shows just the log messages, or if we want to see the log together
|
||||
with the associated patches use the more complex (and much more
|
||||
powerful)
|
||||
|
||||
git-whatchanged -p --root
|
||||
----------------
|
||||
$ git-whatchanged -p --root
|
||||
----------------
|
||||
|
||||
and you will see exactly what has changed in the repository over its
|
||||
short history.
|
||||
@ -547,14 +567,16 @@ it in the `.git/refs/tags/` subdirectory instead of calling it a `head`.
|
||||
So the simplest form of tag involves nothing more than
|
||||
|
||||
------------------------------------------------
|
||||
git tag my-first-tag
|
||||
$ git tag my-first-tag
|
||||
------------------------------------------------
|
||||
|
||||
which just writes the current `HEAD` into the `.git/refs/tags/my-first-tag`
|
||||
file, after which point you can then use this symbolic name for that
|
||||
particular state. You can, for example, do
|
||||
|
||||
git diff my-first-tag
|
||||
----------------
|
||||
$ git diff my-first-tag
|
||||
----------------
|
||||
|
||||
to diff your current state against that tag (which at this point will
|
||||
obviously be an empty diff, but if you continue to develop and commit
|
||||
@ -568,7 +590,9 @@ you really did
|
||||
that tag. You create these annotated tags with either the `-a` or
|
||||
`-s` flag to `git tag`:
|
||||
|
||||
git tag -s <tagname>
|
||||
----------------
|
||||
$ git tag -s <tagname>
|
||||
----------------
|
||||
|
||||
which will sign the current `HEAD` (but you can also give it another
|
||||
argument that specifies the thing to tag, ie you could have tagged the
|
||||
@ -584,8 +608,8 @@ name for the state at that point.
|
||||
Copying repositories
|
||||
--------------------
|
||||
|
||||
git repositories are normally totally self-sufficient, and it's worth noting
|
||||
that unlike CVS, for example, there is no separate notion of
|
||||
git repositories are normally totally self-sufficient and relocatable
|
||||
Unlike CVS, for example, there is no separate notion of
|
||||
"repository" and "working tree". A git repository normally *is* the
|
||||
working tree, with the local git information hidden in the `.git`
|
||||
subdirectory. There is nothing else. What you see is what you got.
|
||||
@ -602,8 +626,10 @@ This has two implications:
|
||||
|
||||
- if you grow bored with the tutorial repository you created (or you've
|
||||
made a mistake and want to start all over), you can just do simple
|
||||
|
||||
rm -rf git-tutorial
|
||||
+
|
||||
----------------
|
||||
$ rm -rf git-tutorial
|
||||
----------------
|
||||
+
|
||||
and it will be gone. There's no external repository, and there's no
|
||||
history outside the project you created.
|
||||
@ -618,8 +644,10 @@ Note that when you've moved or copied a git repository, your git index
|
||||
file (which caches various information, notably some of the "stat"
|
||||
information for the files involved) will likely need to be refreshed.
|
||||
So after you do a `cp -a` to create a new copy, you'll want to do
|
||||
|
||||
git-update-index --refresh
|
||||
+
|
||||
----------------
|
||||
$ git-update-index --refresh
|
||||
----------------
|
||||
+
|
||||
in the new repository to make sure that the index file is up-to-date.
|
||||
|
||||
@ -633,8 +661,10 @@ repositories you often want to make sure that the index cache is in some
|
||||
known state (you don't know *what* they've done and not yet checked in),
|
||||
so usually you'll precede the `git-update-index` with a
|
||||
|
||||
git-read-tree --reset HEAD
|
||||
git-update-index --refresh
|
||||
----------------
|
||||
$ git-read-tree --reset HEAD
|
||||
$ git-update-index --refresh
|
||||
----------------
|
||||
|
||||
which will force a total index re-build from the tree pointed to by `HEAD`.
|
||||
It resets the index contents to `HEAD`, and then the `git-update-index`
|
||||
@ -645,7 +675,9 @@ tells you they need to be updated.
|
||||
|
||||
The above can also be written as simply
|
||||
|
||||
git reset
|
||||
----------------
|
||||
$ git reset
|
||||
----------------
|
||||
|
||||
and in fact a lot of the common git command combinations can be scripted
|
||||
with the `git xyz` interfaces. You can learn things by just looking
|
||||
@ -665,20 +697,26 @@ first create your own subdirectory for the project, and then copy the
|
||||
raw repository contents into the `.git` directory. For example, to
|
||||
create your own copy of the git repository, you'd do the following
|
||||
|
||||
mkdir my-git
|
||||
cd my-git
|
||||
rsync -rL rsync://rsync.kernel.org/pub/scm/git/git.git/ .git
|
||||
----------------
|
||||
$ mkdir my-git
|
||||
$ cd my-git
|
||||
$ rsync -rL rsync://rsync.kernel.org/pub/scm/git/git.git/ .git
|
||||
----------------
|
||||
|
||||
followed by
|
||||
|
||||
git-read-tree HEAD
|
||||
----------------
|
||||
$ git-read-tree HEAD
|
||||
----------------
|
||||
|
||||
to populate the index. However, now you have populated the index, and
|
||||
you have all the git internal files, but you will notice that you don't
|
||||
actually have any of the working tree files to work on. To get
|
||||
those, you'd check them out with
|
||||
|
||||
git-checkout-index -u -a
|
||||
----------------
|
||||
$ git-checkout-index -u -a
|
||||
----------------
|
||||
|
||||
where the `-u` flag means that you want the checkout to keep the index
|
||||
up-to-date (so that you don't have to refresh it afterward), and the
|
||||
@ -689,9 +727,11 @@ files).
|
||||
|
||||
Again, this can all be simplified with
|
||||
|
||||
git clone rsync://rsync.kernel.org/pub/scm/git/git.git/ my-git
|
||||
cd my-git
|
||||
git checkout
|
||||
----------------
|
||||
$ git clone rsync://rsync.kernel.org/pub/scm/git/git.git/ my-git
|
||||
$ cd my-git
|
||||
$ git checkout
|
||||
----------------
|
||||
|
||||
which will end up doing all of the above for you.
|
||||
|
||||
@ -719,7 +759,7 @@ used earlier, and create a branch in it. You do that by simply just
|
||||
saying that you want to check out a new branch:
|
||||
|
||||
------------
|
||||
git checkout -b mybranch
|
||||
$ git checkout -b mybranch
|
||||
------------
|
||||
|
||||
will create a new branch based at the current `HEAD` position, and switch
|
||||
@ -733,7 +773,7 @@ just telling `git checkout` what the base of the checkout would be.
|
||||
In other words, if you have an earlier tag or branch, you'd just do
|
||||
|
||||
------------
|
||||
git checkout -b mybranch earlier-commit
|
||||
$ git checkout -b mybranch earlier-commit
|
||||
------------
|
||||
|
||||
and it would create the new branch `mybranch` at the earlier commit,
|
||||
@ -743,27 +783,27 @@ and check out the state at that time.
|
||||
You can always just jump back to your original `master` branch by doing
|
||||
|
||||
------------
|
||||
git checkout master
|
||||
$ git checkout master
|
||||
------------
|
||||
|
||||
(or any other branch-name, for that matter) and if you forget which
|
||||
branch you happen to be on, a simple
|
||||
|
||||
------------
|
||||
ls -l .git/HEAD
|
||||
$ ls -l .git/HEAD
|
||||
------------
|
||||
|
||||
will tell you where it's pointing (Note that on platforms with bad or no
|
||||
symlink support, you have to execute
|
||||
|
||||
------------
|
||||
cat .git/HEAD
|
||||
$ cat .git/HEAD
|
||||
------------
|
||||
|
||||
instead). To get the list of branches you have, you can say
|
||||
|
||||
------------
|
||||
git branch
|
||||
$ git branch
|
||||
------------
|
||||
|
||||
which is nothing more than a simple script around `ls .git/refs/heads`.
|
||||
@ -773,7 +813,7 @@ Sometimes you may wish to create a new branch _without_ actually
|
||||
checking it out and switching to it. If so, just use the command
|
||||
|
||||
------------
|
||||
git branch <branchname> [startingpoint]
|
||||
$ git branch <branchname> [startingpoint]
|
||||
------------
|
||||
|
||||
which will simply _create_ the branch, but will not do anything further.
|
||||
@ -792,9 +832,9 @@ being the same as the original `master` branch, let's make sure we're in
|
||||
that branch, and do some work there.
|
||||
|
||||
------------------------------------------------
|
||||
git checkout mybranch
|
||||
echo "Work, work, work" >>hello
|
||||
git commit -m 'Some work.' hello
|
||||
$ git checkout mybranch
|
||||
$ echo "Work, work, work" >>hello
|
||||
$ git commit -m 'Some work.' hello
|
||||
------------------------------------------------
|
||||
|
||||
Here, we just added another line to `hello`, and we used a shorthand for
|
||||
@ -807,7 +847,7 @@ does some work in the original branch, and simulate that by going back
|
||||
to the master branch, and editing the same file differently there:
|
||||
|
||||
------------
|
||||
git checkout master
|
||||
$ git checkout master
|
||||
------------
|
||||
|
||||
Here, take a moment to look at the contents of `hello`, and notice how they
|
||||
@ -815,9 +855,9 @@ don't contain the work we just did in `mybranch` -- because that work
|
||||
hasn't happened in the `master` branch at all. Then do
|
||||
|
||||
------------
|
||||
echo "Play, play, play" >>hello
|
||||
echo "Lots of fun" >>example
|
||||
git commit -m 'Some fun.' hello example
|
||||
$ echo "Play, play, play" >>hello
|
||||
$ echo "Lots of fun" >>example
|
||||
$ git commit -m 'Some fun.' hello example
|
||||
------------
|
||||
|
||||
since the master branch is obviously in a much better mood.
|
||||
@ -826,7 +866,9 @@ Now, you've got two branches, and you decide that you want to merge the
|
||||
work done. Before we do that, let's introduce a cool graphical tool that
|
||||
helps you view what's going on:
|
||||
|
||||
gitk --all
|
||||
----------------
|
||||
$ gitk --all
|
||||
----------------
|
||||
|
||||
will show you graphically both of your branches (that's what the `\--all`
|
||||
means: normally it will just show you your current `HEAD`) and their
|
||||
@ -836,14 +878,14 @@ source.
|
||||
Anyway, let's exit `gitk` (`^Q` or the File menu), and decide that we want
|
||||
to merge the work we did on the `mybranch` branch into the `master`
|
||||
branch (which is currently our `HEAD` too). To do that, there's a nice
|
||||
script called `git resolve`, which wants to know which branches you want
|
||||
script called `git merge`, which wants to know which branches you want
|
||||
to resolve and what the merge is all about:
|
||||
|
||||
------------
|
||||
git resolve HEAD mybranch "Merge work in mybranch"
|
||||
$ git merge "Merge work in mybranch" HEAD mybranch
|
||||
------------
|
||||
|
||||
where the third argument is going to be used as the commit message if
|
||||
where the first argument is going to be used as the commit message if
|
||||
the merge can be resolved automatically.
|
||||
|
||||
Now, in this case we've intentionally created a situation where the
|
||||
@ -851,12 +893,16 @@ merge will need to be fixed up by hand, though, so git will do as much
|
||||
of it as it can automatically (which in this case is just merge the `example`
|
||||
file, which had no differences in the `mybranch` branch), and say:
|
||||
|
||||
Simple merge failed, trying Automatic merge
|
||||
Auto-merging hello.
|
||||
----------------
|
||||
Trying really trivial in-index merge...
|
||||
fatal: Merge requires file-level merging
|
||||
Nope.
|
||||
...
|
||||
merge: warning: conflicts during merge
|
||||
ERROR: Merge conflict in hello.
|
||||
fatal: merge program failed
|
||||
Automatic merge failed, fix up by hand
|
||||
Automatic merge failed/prevented; fix up by hand
|
||||
----------------
|
||||
|
||||
which is way too verbose, but it basically tells you that it failed the
|
||||
really trivial merge ("Simple merge") and did an "Automatic merge"
|
||||
@ -877,7 +923,7 @@ Work, work, work
|
||||
and once you're happy with your manual merge, just do a
|
||||
|
||||
------------
|
||||
git commit hello
|
||||
$ git commit hello
|
||||
------------
|
||||
|
||||
which will very loudly warn you that you're now committing a merge
|
||||
@ -927,17 +973,19 @@ to the `master` branch. Let's go back to `mybranch`, and run
|
||||
resolve to get the "upstream changes" back to your branch.
|
||||
|
||||
------------
|
||||
git checkout mybranch
|
||||
git resolve HEAD master "Merge upstream changes."
|
||||
$ git checkout mybranch
|
||||
$ git merge "Merge upstream changes." HEAD master
|
||||
------------
|
||||
|
||||
This outputs something like this (the actual commit object names
|
||||
would be different)
|
||||
|
||||
Updating from ae3a2da... to a80b4aa....
|
||||
example | 1 +
|
||||
hello | 1 +
|
||||
2 files changed, 2 insertions(+), 0 deletions(-)
|
||||
----------------
|
||||
Updating from ae3a2da... to a80b4aa....
|
||||
example | 1 +
|
||||
hello | 1 +
|
||||
2 files changed, 2 insertions(+), 0 deletions(-)
|
||||
----------------
|
||||
|
||||
Because your branch did not contain anything more than what are
|
||||
already merged into the `master` branch, the resolve operation did
|
||||
@ -963,14 +1011,16 @@ Merging external work
|
||||
It's usually much more common that you merge with somebody else than
|
||||
merging with your own branches, so it's worth pointing out that git
|
||||
makes that very easy too, and in fact, it's not that different from
|
||||
doing a `git resolve`. In fact, a remote merge ends up being nothing
|
||||
doing a `git merge`. In fact, a remote merge ends up being nothing
|
||||
more than "fetch the work from a remote repository into a temporary tag"
|
||||
followed by a `git resolve`.
|
||||
followed by a `git merge`.
|
||||
|
||||
Fetching from a remote repository is done by, unsurprisingly,
|
||||
`git fetch`:
|
||||
|
||||
git fetch <remote-repository>
|
||||
----------------
|
||||
$ git fetch <remote-repository>
|
||||
----------------
|
||||
|
||||
One of the following transports can be used to name the
|
||||
repository to download from:
|
||||
@ -1015,7 +1065,7 @@ This transport was designed for anonymous downloading. Like SSH
|
||||
transport, it finds out the set of objects the downstream side
|
||||
lacks and transfers (close to) minimum set of objects.
|
||||
|
||||
HTTP(s)::
|
||||
HTTP(S)::
|
||||
`http://remote.machine/path/to/repo.git/`
|
||||
+
|
||||
HTTP and HTTPS transport are used only for downloading. They
|
||||
@ -1045,7 +1095,9 @@ However -- it's such a common thing to `fetch` and then
|
||||
immediately `resolve`, that it's called `git pull`, and you can
|
||||
simply do
|
||||
|
||||
git pull <remote-repository>
|
||||
----------------
|
||||
$ git pull <remote-repository>
|
||||
----------------
|
||||
|
||||
and optionally give a branch-name for the remote end as a second
|
||||
argument.
|
||||
@ -1073,8 +1125,8 @@ the remote repository URL in a file under .git/remotes/
|
||||
directory, like this:
|
||||
|
||||
------------------------------------------------
|
||||
mkdir -p .git/remotes/
|
||||
cat >.git/remotes/linus <<\EOF
|
||||
$ mkdir -p .git/remotes/
|
||||
$ cat >.git/remotes/linus <<\EOF
|
||||
URL: http://www.kernel.org/pub/scm/git/git.git/
|
||||
EOF
|
||||
------------------------------------------------
|
||||
@ -1084,7 +1136,7 @@ The URL specified in such file can even be a prefix
|
||||
of a full URL, like this:
|
||||
|
||||
------------------------------------------------
|
||||
cat >.git/remotes/jgarzik <<\EOF
|
||||
$ cat >.git/remotes/jgarzik <<\EOF
|
||||
URL: http://www.kernel.org/pub/scm/linux/git/jgarzik/
|
||||
EOF
|
||||
------------------------------------------------
|
||||
@ -1103,6 +1155,156 @@ the above are equivalent to:
|
||||
. `git pull http://www.kernel.org/pub/.../jgarzik/netdev-2.6.git e100`
|
||||
|
||||
|
||||
How does the merge work?
|
||||
------------------------
|
||||
|
||||
We said this tutorial shows what plumbing does to help you cope
|
||||
with the porcelain that isn't flushing, but we so far did not
|
||||
talk about how the merge really works. If you are following
|
||||
this tutorial the first time, I'd suggest to skip to "Publishing
|
||||
your work" section and come back here later.
|
||||
|
||||
OK, still with me? To give us an example to look at, let's go
|
||||
back to the earlier repository with "hello" and "example" file,
|
||||
and bring ourselves back to the pre-merge state:
|
||||
|
||||
------------
|
||||
$ git show-branch --more=3 master mybranch
|
||||
! [master] Merge work in mybranch
|
||||
* [mybranch] Merge work in mybranch
|
||||
--
|
||||
++ [master] Merge work in mybranch
|
||||
++ [master^2] Some work.
|
||||
++ [master^] Some fun.
|
||||
------------
|
||||
|
||||
Remember, before running `git merge`, our `master` head was at
|
||||
"Some fun." commit, while our `mybranch` head was at "Some
|
||||
work." commit.
|
||||
|
||||
------------
|
||||
$ git checkout mybranch
|
||||
$ git reset --hard master^2
|
||||
$ git checkout master
|
||||
$ git reset --hard master^
|
||||
------------
|
||||
|
||||
After rewinding, the commit structure should look like this:
|
||||
|
||||
------------
|
||||
$ git show-branch
|
||||
* [master] Some fun.
|
||||
! [mybranch] Some work.
|
||||
--
|
||||
+ [mybranch] Some work.
|
||||
+ [master] Some fun.
|
||||
++ [mybranch^] New day.
|
||||
------------
|
||||
|
||||
Now we are ready to experiment with the merge by hand.
|
||||
|
||||
`git merge` command, when merging two branches, uses 3-way merge
|
||||
algorithm. First, it finds the common ancestor between them.
|
||||
The command it uses is `git-merge-base`:
|
||||
|
||||
------------
|
||||
$ mb=$(git-merge-base HEAD mybranch)
|
||||
------------
|
||||
|
||||
The command writes the commit object name of the common ancestor
|
||||
to the standard output, so we captured its output to a variable,
|
||||
because we will be using it in the next step. BTW, the common
|
||||
ancestor commit is the "New day." commit in this case. You can
|
||||
tell it by:
|
||||
|
||||
------------
|
||||
$ git-name-rev $mb
|
||||
my-first-tag
|
||||
------------
|
||||
|
||||
After finding out a common ancestor commit, the second step is
|
||||
this:
|
||||
|
||||
------------
|
||||
$ git-read-tree -m -u $mb HEAD mybranch
|
||||
------------
|
||||
|
||||
This is the same `git-read-tree` command we have already seen,
|
||||
but it takes three trees, unlike previous examples. This reads
|
||||
the contents of each tree into different 'stage' in the index
|
||||
file (the first tree goes to stage 1, the second stage 2,
|
||||
etc.). After reading three trees into three stages, the paths
|
||||
that are the same in all three stages are 'collapsed' into stage
|
||||
0. Also paths that are the same in two of three stages are
|
||||
collapsed into stage 0, taking the SHA1 from either stage 2 or
|
||||
stage 3, whichever is different from stage 1 (i.e. only one side
|
||||
changed from the common ancestor).
|
||||
|
||||
After 'collapsing' operation, paths that are different in three
|
||||
trees are left in non-zero stages. At this point, you can
|
||||
inspect the index file with this command:
|
||||
|
||||
------------
|
||||
$ git-ls-files --stage
|
||||
100644 7f8b141b65fdcee47321e399a2598a235a032422 0 example
|
||||
100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1 hello
|
||||
100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2 hello
|
||||
100644 cc44c73eb783565da5831b4d820c962954019b69 3 hello
|
||||
------------
|
||||
|
||||
In our example of only two files, we did not have unchanged
|
||||
files so only 'example' resulted in collapsing, but in real-life
|
||||
large projects, only small number of files change in one commit,
|
||||
and this 'collapsing' tends to trivially merge most of the paths
|
||||
fairly quickly, leaving only a handful the real changes in non-zero
|
||||
stages.
|
||||
|
||||
To look at only non-zero stages, use `\--unmerged` flag:
|
||||
|
||||
------------
|
||||
$ git-ls-files --unmerged
|
||||
100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1 hello
|
||||
100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2 hello
|
||||
100644 cc44c73eb783565da5831b4d820c962954019b69 3 hello
|
||||
------------
|
||||
|
||||
The next step of merging is to merge these three versions of the
|
||||
file, using 3-way merge. This is done by giving
|
||||
`git-merge-one-file` command as one of the arguments to
|
||||
`git-merge-index` command:
|
||||
|
||||
------------
|
||||
$ git-merge-index git-merge-one-file hello
|
||||
Auto-merging hello.
|
||||
merge: warning: conflicts during merge
|
||||
ERROR: Merge conflict in hello.
|
||||
fatal: merge program failed
|
||||
------------
|
||||
|
||||
`git-merge-one-file` script is called with parameters to
|
||||
describe those three versions, and is responsible to leave the
|
||||
merge results in the working tree and register it in the index
|
||||
file. It is a fairly straightforward shell script, and
|
||||
eventually calls `merge` program from RCS suite to perform the
|
||||
file-level 3-way merge. In this case, `merge` detects
|
||||
conflicts, and the merge result with conflict marks is left in
|
||||
the working tree, while the index file is updated with the
|
||||
version from the current branch (this is to make `git diff`
|
||||
useful after this step). This can be seen if you run `ls-files
|
||||
--stage` again at this point:
|
||||
|
||||
------------
|
||||
$ git-ls-files --stage
|
||||
100644 7f8b141b65fdcee47321e399a2598a235a032422 0 example
|
||||
100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 0 hello
|
||||
------------
|
||||
|
||||
As you can see, there is no unmerged paths in the index file.
|
||||
This is the state of the index file and the working file after
|
||||
`git merge` returns control back to you, leaving the conflicting
|
||||
merge for you to resolve.
|
||||
|
||||
|
||||
Publishing your work
|
||||
--------------------
|
||||
|
||||
@ -1148,7 +1350,7 @@ project `my-git`. After logging into the remote machine, create
|
||||
an empty directory:
|
||||
|
||||
------------
|
||||
mkdir my-git.git
|
||||
$ mkdir my-git.git
|
||||
------------
|
||||
|
||||
Then, make that directory into a git repository by running
|
||||
@ -1156,7 +1358,7 @@ Then, make that directory into a git repository by running
|
||||
`.git`, we do things slightly differently:
|
||||
|
||||
------------
|
||||
GIT_DIR=my-git.git git-init-db
|
||||
$ GIT_DIR=my-git.git git-init-db
|
||||
------------
|
||||
|
||||
Make sure this directory is available for others you want your
|
||||
@ -1182,7 +1384,7 @@ Come back to the machine you have your private repository. From
|
||||
there, run this command:
|
||||
|
||||
------------
|
||||
git push <public-host>:/path/to/my-git.git master
|
||||
$ git push <public-host>:/path/to/my-git.git master
|
||||
------------
|
||||
|
||||
This synchronizes your public repository to match the named
|
||||
@ -1194,7 +1396,7 @@ repository. Kernel.org mirror network takes care of the
|
||||
propagation to other publicly visible machines:
|
||||
|
||||
------------
|
||||
git push master.kernel.org:/pub/scm/git/git.git/
|
||||
$ git push master.kernel.org:/pub/scm/git/git.git/
|
||||
------------
|
||||
|
||||
|
||||
@ -1209,7 +1411,7 @@ immutable once they are created, there is a way to optimize the
|
||||
storage by "packing them together". The command
|
||||
|
||||
------------
|
||||
git repack
|
||||
$ git repack
|
||||
------------
|
||||
|
||||
will do it for you. If you followed the tutorial examples, you
|
||||
@ -1235,7 +1437,7 @@ Once you have packed objects, you do not need to leave the
|
||||
unpacked objects that are contained in the pack file anymore.
|
||||
|
||||
------------
|
||||
git prune-packed
|
||||
$ git prune-packed
|
||||
------------
|
||||
|
||||
would remove them for you.
|
||||
@ -1464,8 +1666,8 @@ in both of them. You could merge in 'diff-fix' first and then
|
||||
'commit-fix' next, like this:
|
||||
|
||||
------------
|
||||
$ git resolve master diff-fix 'Merge fix in diff-fix'
|
||||
$ git resolve master commit-fix 'Merge fix in commit-fix'
|
||||
$ git merge 'Merge fix in diff-fix' master diff-fix
|
||||
$ git merge 'Merge fix in commit-fix' master commit-fix
|
||||
------------
|
||||
|
||||
Which would result in:
|
||||
@ -1498,8 +1700,8 @@ $ git reset --hard master~2
|
||||
------------
|
||||
|
||||
You can make sure 'git show-branch' matches the state before
|
||||
those two 'git resolve' you just did. Then, instead of running
|
||||
two 'git resolve' commands in a row, you would pull these two
|
||||
those two 'git merge' you just did. Then, instead of running
|
||||
two 'git merge' commands in a row, you would pull these two
|
||||
branch heads (this is known as 'making an Octopus'):
|
||||
|
||||
------------
|
||||
|
21
INSTALL
21
INSTALL
@ -5,10 +5,13 @@ Normally you can just do "make" followed by "make install", and that
|
||||
will install the git programs in your own ~/bin/ directory. If you want
|
||||
to do a global install, you can do
|
||||
|
||||
make prefix=/usr install
|
||||
$ make prefix=/usr ;# as yourself
|
||||
# make prefix=/usr install ;# as root
|
||||
|
||||
(or prefix=/usr/local, of course). Some day somebody may send me a RPM
|
||||
spec file or something, and you can do "make rpm" or whatever.
|
||||
(or prefix=/usr/local, of course). Just like any program suite
|
||||
that uses $prefix, the built results have some paths encoded,
|
||||
which are derived from $prefix, so "make all; make prefix=/usr
|
||||
install" would not work.
|
||||
|
||||
Issues of note:
|
||||
|
||||
@ -72,3 +75,15 @@ Issues of note:
|
||||
history graphically
|
||||
|
||||
- "ssh" is used to push and pull over the net
|
||||
|
||||
- "perl" and POSIX-compliant shells are needed to use most of
|
||||
the barebone Porcelainish scripts.
|
||||
|
||||
- "python" 2.3 or more recent; if you have 2.3, you may need
|
||||
to build with "make WITH_OWN_SUBPROCESS_PY=YesPlease".
|
||||
|
||||
- Some platform specific issues are dealt with Makefile rules,
|
||||
but depending on your specific installation, you may not
|
||||
have all the libraries/tools needed, or you may have
|
||||
necessary libraries at unusual locations. Please look at the
|
||||
top of the Makefile to see what can be adjusted for your needs.
|
||||
|
41
Makefile
41
Makefile
@ -50,11 +50,14 @@
|
||||
# Define USE_STDEV below if you want git to care about the underlying device
|
||||
# change being considered an inode change from the update-cache perspective.
|
||||
|
||||
GIT_VERSION = 0.99.9e
|
||||
GIT_VERSION = 0.99.9i
|
||||
|
||||
# CFLAGS and LDFLAGS are for the users to override from the command line.
|
||||
|
||||
# CFLAGS is for the users to override from the command line.
|
||||
CFLAGS = -g -O2 -Wall
|
||||
LDFLAGS =
|
||||
ALL_CFLAGS = $(CFLAGS)
|
||||
ALL_LDFLAGS = $(LDFLAGS)
|
||||
|
||||
prefix = $(HOME)
|
||||
bindir = $(prefix)/bin
|
||||
@ -88,12 +91,13 @@ SCRIPT_SH = \
|
||||
git-tag.sh git-verify-tag.sh git-whatchanged.sh git.sh \
|
||||
git-applymbox.sh git-applypatch.sh git-am.sh \
|
||||
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
|
||||
git-merge-resolve.sh git-merge-ours.sh git-grep.sh
|
||||
git-merge-resolve.sh git-merge-ours.sh git-grep.sh \
|
||||
git-lost-found.sh
|
||||
|
||||
SCRIPT_PERL = \
|
||||
git-archimport.perl git-cvsimport.perl git-relink.perl \
|
||||
git-rename.perl git-shortlog.perl git-fmt-merge-msg.perl \
|
||||
git-svnimport.perl git-mv.perl
|
||||
git-shortlog.perl git-fmt-merge-msg.perl \
|
||||
git-svnimport.perl git-mv.perl git-cvsexportcommit.perl
|
||||
|
||||
SCRIPT_PYTHON = \
|
||||
git-merge-recursive.py
|
||||
@ -121,7 +125,7 @@ PROGRAMS = \
|
||||
git-unpack-objects$X git-update-index$X git-update-server-info$X \
|
||||
git-upload-pack$X git-verify-pack$X git-write-tree$X \
|
||||
git-update-ref$X git-symbolic-ref$X git-check-ref-format$X \
|
||||
git-name-rev$X $(SIMPLE_PROGRAMS)
|
||||
git-name-rev$X git-pack-redundant$X $(SIMPLE_PROGRAMS)
|
||||
|
||||
# Backward compatibility -- to be removed after 1.0
|
||||
PROGRAMS += git-ssh-pull$X git-ssh-push$X
|
||||
@ -185,9 +189,11 @@ ifeq ($(uname_S),Darwin)
|
||||
NEEDS_SSL_WITH_CRYPTO = YesPlease
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
## fink
|
||||
ALL_CFLAGS += -I/sw/include -L/sw/lib
|
||||
ALL_CFLAGS += -I/sw/include
|
||||
ALL_LDFLAGS += -L/sw/lib
|
||||
## darwinports
|
||||
ALL_CFLAGS += -I/opt/local/include -L/opt/local/lib
|
||||
ALL_CFLAGS += -I/opt/local/include
|
||||
ALL_LDFLAGS += -L/opt/local/lib
|
||||
endif
|
||||
ifeq ($(uname_S),SunOS)
|
||||
NEEDS_SOCKET = YesPlease
|
||||
@ -209,7 +215,13 @@ endif
|
||||
ifeq ($(uname_S),OpenBSD)
|
||||
NO_STRCASESTR = YesPlease
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
ALL_CFLAGS += -I/usr/local/include -L/usr/local/lib
|
||||
ALL_CFLAGS += -I/usr/local/include
|
||||
ALL_LDFLAGS += -L/usr/local/lib
|
||||
endif
|
||||
ifeq ($(uname_S),NetBSD)
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
ALL_CFLAGS += -I/usr/pkg/include
|
||||
ALL_LDFLAGS += -L/usr/pkg/lib -Wl,-rpath,/usr/pkg/lib
|
||||
endif
|
||||
ifneq (,$(findstring arm,$(uname_M)))
|
||||
ARM_SHA1 = YesPlease
|
||||
@ -219,7 +231,7 @@ endif
|
||||
|
||||
ifndef NO_CURL
|
||||
ifdef CURLDIR
|
||||
# This is still problematic -- gcc does not want -R.
|
||||
# This is still problematic -- gcc does not always want -R.
|
||||
ALL_CFLAGS += -I$(CURLDIR)/include
|
||||
CURL_LIBCURL = -L$(CURLDIR)/lib -R$(CURLDIR)/lib -lcurl
|
||||
else
|
||||
@ -367,12 +379,13 @@ git-cherry-pick: git-revert
|
||||
$(CC) -o $*.o -c $(ALL_CFLAGS) $<
|
||||
|
||||
git-%$X: %.o $(LIB_FILE)
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(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) : git-%$X : %.o
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(filter %.o,$^) $(LIB_FILE) $(SIMPLE_LIB)
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
||||
$(LIB_FILE) $(SIMPLE_LIB)
|
||||
|
||||
git-http-fetch$X: fetch.o
|
||||
git-local-fetch$X: fetch.o
|
||||
@ -406,10 +419,10 @@ test: all
|
||||
$(MAKE) -C t/ all
|
||||
|
||||
test-date$X: test-date.c date.o ctype.o
|
||||
$(CC) $(ALL_CFLAGS) -o $@ test-date.c date.o ctype.o
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) test-date.c date.o ctype.o
|
||||
|
||||
test-delta$X: test-delta.c diff-delta.o patch-delta.o
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $^
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^
|
||||
|
||||
check:
|
||||
for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i; done
|
||||
|
43
apply.c
43
apply.c
@ -23,10 +23,11 @@ static int numstat = 0;
|
||||
static int summary = 0;
|
||||
static int check = 0;
|
||||
static int apply = 1;
|
||||
static int no_add = 0;
|
||||
static int show_index_info = 0;
|
||||
static int line_termination = '\n';
|
||||
static const char apply_usage[] =
|
||||
"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--index-info] [-z] <patch>...";
|
||||
"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [-z] <patch>...";
|
||||
|
||||
/*
|
||||
* For "diff-stat" like behaviour, we keep track of the biggest change
|
||||
@ -53,7 +54,7 @@ struct fragment {
|
||||
struct patch {
|
||||
char *new_name, *old_name, *def_name;
|
||||
unsigned int old_mode, new_mode;
|
||||
int is_rename, is_copy, is_new, is_delete;
|
||||
int is_rename, is_copy, is_new, is_delete, is_binary;
|
||||
int lines_added, lines_deleted;
|
||||
int score;
|
||||
struct fragment *fragments;
|
||||
@ -369,7 +370,7 @@ static int gitdiff_index(const char *line, struct patch *patch)
|
||||
int len;
|
||||
|
||||
ptr = strchr(line, '.');
|
||||
if (!ptr || ptr[1] != '.' || 40 <= ptr - line)
|
||||
if (!ptr || ptr[1] != '.' || 40 < ptr - line)
|
||||
return 0;
|
||||
len = ptr - line;
|
||||
memcpy(patch->old_sha1_prefix, line, len);
|
||||
@ -383,7 +384,7 @@ static int gitdiff_index(const char *line, struct patch *patch)
|
||||
ptr = eol;
|
||||
len = ptr - line;
|
||||
|
||||
if (40 <= len)
|
||||
if (40 < len)
|
||||
return 0;
|
||||
memcpy(patch->new_sha1_prefix, line, len);
|
||||
patch->new_sha1_prefix[len] = 0;
|
||||
@ -890,8 +891,19 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
|
||||
|
||||
patchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch);
|
||||
|
||||
if (!patchsize && !metadata_changes(patch))
|
||||
die("patch with only garbage at line %d", linenr);
|
||||
if (!patchsize && !metadata_changes(patch)) {
|
||||
static const char binhdr[] = "Binary files ";
|
||||
|
||||
if (sizeof(binhdr) - 1 < size - offset - hdrsize &&
|
||||
!memcmp(binhdr, buffer + hdrsize + offset,
|
||||
sizeof(binhdr)-1))
|
||||
patch->is_binary = 1;
|
||||
|
||||
if (patch->is_binary && !apply && !check)
|
||||
;
|
||||
else
|
||||
die("patch with only garbage at line %d", linenr);
|
||||
}
|
||||
|
||||
return offset + hdrsize + patchsize;
|
||||
}
|
||||
@ -949,9 +961,12 @@ static void show_stats(struct patch *patch)
|
||||
add = (add * max + max_change / 2) / max_change;
|
||||
del = total - add;
|
||||
}
|
||||
printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
|
||||
len, name, patch->lines_added + patch->lines_deleted,
|
||||
add, pluses, del, minuses);
|
||||
if (patch->is_binary)
|
||||
printf(" %s%-*s | Bin\n", prefix, len, name);
|
||||
else
|
||||
printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
|
||||
len, name, patch->lines_added + patch->lines_deleted,
|
||||
add, pluses, del, minuses);
|
||||
if (qname)
|
||||
free(qname);
|
||||
}
|
||||
@ -1099,8 +1114,10 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
|
||||
break;
|
||||
/* Fall-through for ' ' */
|
||||
case '+':
|
||||
memcpy(new + newsize, patch + 1, plen);
|
||||
newsize += plen;
|
||||
if (*patch != '+' || !no_add) {
|
||||
memcpy(new + newsize, patch + 1, plen);
|
||||
newsize += plen;
|
||||
}
|
||||
break;
|
||||
case '@': case '\\':
|
||||
/* Ignore it, we already handled it */
|
||||
@ -1697,6 +1714,10 @@ int main(int argc, char **argv)
|
||||
excludes = x;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--no-add")) {
|
||||
no_add = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--stat")) {
|
||||
apply = 0;
|
||||
diffstat = 1;
|
||||
|
42
commit.c
42
commit.c
@ -34,6 +34,8 @@ enum cmit_fmt get_commit_format(const char *arg)
|
||||
return CMIT_FMT_SHORT;
|
||||
if (!strcmp(arg, "=full"))
|
||||
return CMIT_FMT_FULL;
|
||||
if (!strcmp(arg, "=fuller"))
|
||||
return CMIT_FMT_FULLER;
|
||||
if (!strcmp(arg, "=oneline"))
|
||||
return CMIT_FMT_ONELINE;
|
||||
die("invalid --pretty format");
|
||||
@ -361,6 +363,7 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
|
||||
int namelen;
|
||||
unsigned long time;
|
||||
int tz, ret;
|
||||
const char *filler = " ";
|
||||
|
||||
if (fmt == CMIT_FMT_ONELINE)
|
||||
return 0;
|
||||
@ -371,9 +374,20 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
|
||||
time = strtoul(date, &date, 10);
|
||||
tz = strtol(date, NULL, 10);
|
||||
|
||||
ret = sprintf(buf, "%s: %.*s\n", what, namelen, line);
|
||||
if (fmt == CMIT_FMT_MEDIUM)
|
||||
ret = sprintf(buf, "%s: %.*s%.*s\n", what,
|
||||
(fmt == CMIT_FMT_FULLER) ? 4 : 0,
|
||||
filler, namelen, line);
|
||||
switch (fmt) {
|
||||
case CMIT_FMT_MEDIUM:
|
||||
ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz));
|
||||
break;
|
||||
case CMIT_FMT_FULLER:
|
||||
ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz));
|
||||
break;
|
||||
default:
|
||||
/* notin' */
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -448,12 +462,21 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const char *msg, unsigned l
|
||||
die("bad parent line in commit");
|
||||
offset += add_parent_info(fmt, buf + offset, line, ++parents);
|
||||
}
|
||||
|
||||
/*
|
||||
* MEDIUM == DEFAULT shows only author with dates.
|
||||
* FULL shows both authors but not dates.
|
||||
* FULLER shows both authors and dates.
|
||||
*/
|
||||
if (!memcmp(line, "author ", 7))
|
||||
offset += add_user_info("Author", fmt, buf + offset, line + 7);
|
||||
if (fmt == CMIT_FMT_FULL) {
|
||||
if (!memcmp(line, "committer ", 10))
|
||||
offset += add_user_info("Commit", fmt, buf + offset, line + 10);
|
||||
}
|
||||
offset += add_user_info("Author", fmt,
|
||||
buf + offset,
|
||||
line + 7);
|
||||
if (!memcmp(line, "committer ", 10) &&
|
||||
(fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER))
|
||||
offset += add_user_info("Commit", fmt,
|
||||
buf + offset,
|
||||
line + 10);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -513,7 +536,7 @@ int count_parents(struct commit * commit)
|
||||
void sort_in_topological_order(struct commit_list ** list)
|
||||
{
|
||||
struct commit_list * next = *list;
|
||||
struct commit_list * work = NULL;
|
||||
struct commit_list * work = NULL, **insert;
|
||||
struct commit_list ** pptr = list;
|
||||
struct sort_node * nodes;
|
||||
struct sort_node * next_nodes;
|
||||
@ -557,11 +580,12 @@ void sort_in_topological_order(struct commit_list ** list)
|
||||
* the tips serve as a starting set for the work queue.
|
||||
*/
|
||||
next=*list;
|
||||
insert = &work;
|
||||
while (next) {
|
||||
struct sort_node * node = (struct sort_node *)next->item->object.util;
|
||||
|
||||
if (node->indegree == 0) {
|
||||
commit_list_insert(next->item, &work);
|
||||
insert = &commit_list_insert(next->item, insert)->next;
|
||||
}
|
||||
next=next->next;
|
||||
}
|
||||
|
1
commit.h
1
commit.h
@ -43,6 +43,7 @@ enum cmit_fmt {
|
||||
CMIT_FMT_DEFAULT = CMIT_FMT_MEDIUM,
|
||||
CMIT_FMT_SHORT,
|
||||
CMIT_FMT_FULL,
|
||||
CMIT_FMT_FULLER,
|
||||
CMIT_FMT_ONELINE,
|
||||
};
|
||||
|
||||
|
38
debian/changelog
vendored
38
debian/changelog
vendored
@ -1,3 +1,27 @@
|
||||
git-core (0.99.9i-0) unstable; urgency=low
|
||||
|
||||
* GIT 0.99.9i aka 1.0rc2
|
||||
|
||||
-- Junio C Hamano <junkio@cox.net> Mon, 14 Nov 2005 18:38:27 -0800
|
||||
|
||||
git-core (0.99.9h-0) unstable; urgency=low
|
||||
|
||||
* GIT 0.99.9h
|
||||
|
||||
-- Junio C Hamano <junkio@cox.net> Fri, 11 Nov 2005 22:33:18 -0800
|
||||
|
||||
git-core (0.99.9g-0) unstable; urgency=low
|
||||
|
||||
* GIT 0.99.9g
|
||||
|
||||
-- Junio C Hamano <junkio@cox.net> Wed, 9 Nov 2005 21:01:55 -0800
|
||||
|
||||
git-core (0.99.9f-0) unstable; urgency=low
|
||||
|
||||
* GIT 0.99.9f
|
||||
|
||||
-- Junio C Hamano <junkio@cox.net> Tue, 8 Nov 2005 01:21:52 -0800
|
||||
|
||||
git-core (0.99.9e-0) unstable; urgency=low
|
||||
|
||||
* GIT 0.99.9e
|
||||
@ -28,6 +52,20 @@ git-core (0.99.9a-0) unstable; urgency=low
|
||||
|
||||
-- Junio C Hamano <junkio@cox.net> Sun, 30 Oct 2005 15:03:32 -0800
|
||||
|
||||
git-core (0.99.9.GIT-2) unstable; urgency=low
|
||||
|
||||
* Build Dependency did not include libexpat-dev.
|
||||
|
||||
-- Junio C Hamano <junkio@cox.net> Sun, 13 Nov 2005 01:55:34 -0800
|
||||
|
||||
git-core (0.99.9.GIT-1) unstable; urgency=low
|
||||
|
||||
* Do not scatter txt and html documentation into feature
|
||||
subpackages. Do place man pages into them.
|
||||
* Capture more cvs stuff into git-cvs package.
|
||||
|
||||
-- Junio C Hamano <junkio@cox.net> Tue, 8 Nov 2005 01:19:06 -0800
|
||||
|
||||
git-core (0.99.9.GIT-0) unstable; urgency=low
|
||||
|
||||
* Test Build.
|
||||
|
4
debian/control
vendored
4
debian/control
vendored
@ -2,7 +2,7 @@ Source: git-core
|
||||
Section: devel
|
||||
Priority: optional
|
||||
Maintainer: Junio C Hamano <junkio@cox.net>
|
||||
Build-Depends-Indep: libz-dev, libssl-dev, libcurl3-dev|libcurl3-gnutls-dev|libcurl3-openssl-dev, asciidoc (>= 6.0.3), xmlto, debhelper (>= 4.0.0), bc
|
||||
Build-Depends-Indep: libz-dev, libssl-dev, libcurl3-dev|libcurl3-gnutls-dev|libcurl3-openssl-dev, asciidoc (>= 7), xmlto, debhelper (>= 4.0.0), bc, libexpat-dev
|
||||
Standards-Version: 3.6.1
|
||||
|
||||
Package: git-core
|
||||
@ -48,7 +48,7 @@ Description: The git content addressable filesystem, GNUArch interoperability
|
||||
|
||||
Package: git-cvs
|
||||
Architecture: all
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, git-core
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, git-core, cvsps (>= 2.1)
|
||||
Suggests: cvs
|
||||
Description: The git content addressable filesystem, CVS interoperability
|
||||
This package contains 'git-cvsimport', to import development history from
|
||||
|
4
debian/git-arch.files
vendored
4
debian/git-arch.files
vendored
@ -1,2 +1,2 @@
|
||||
/usr/bin/git-archimport
|
||||
/usr/share/doc/git-core/git-archimport.*
|
||||
/usr/bin/git-arch*
|
||||
/usr/share/man/*/git-arch*
|
||||
|
4
debian/git-core.doc-base
vendored
4
debian/git-core.doc-base
vendored
@ -6,6 +6,8 @@ Section: Devel
|
||||
Format: HTML
|
||||
Index: /usr/share/doc/git-core/git.html
|
||||
Files: /usr/share/doc/git-core/*.html
|
||||
/usr/share/doc/git-core/*/*.html
|
||||
|
||||
Format: text
|
||||
Files: /usr/share/doc/git-core/git.txt*
|
||||
Files: /usr/share/doc/git-core/*.txt
|
||||
/usr/share/doc/git-core/*/*.txt
|
||||
|
4
debian/git-cvs.files
vendored
4
debian/git-cvs.files
vendored
@ -1,2 +1,2 @@
|
||||
/usr/bin/git-cvsimport
|
||||
/usr/share/doc/git-core/git-cvsimport.*
|
||||
/usr/bin/git-cvs*
|
||||
/usr/share/man/*/git-cvs*
|
||||
|
3
debian/git-doc.files
vendored
3
debian/git-doc.files
vendored
@ -2,6 +2,3 @@
|
||||
/usr/share/doc/git-core/*.html
|
||||
/usr/share/doc/git-core/*/*.html
|
||||
/usr/share/doc/git-core/*/*.txt
|
||||
|
||||
|
||||
|
||||
|
2
debian/git-email.files
vendored
2
debian/git-email.files
vendored
@ -1,2 +1,2 @@
|
||||
/usr/bin/git-send-email
|
||||
/usr/share/doc/git-core/git-send-email.*
|
||||
/usr/share/man/*/git-send-email.*
|
||||
|
4
debian/git-svn.files
vendored
4
debian/git-svn.files
vendored
@ -1,2 +1,2 @@
|
||||
/usr/bin/git-svnimport
|
||||
/usr/share/doc/git-core/git-svnimport.*
|
||||
/usr/bin/git-svn*
|
||||
/usr/share/man/*/git-svn*
|
||||
|
1
debian/git-tk.files
vendored
1
debian/git-tk.files
vendored
@ -1,3 +1,2 @@
|
||||
/usr/bin/gitk
|
||||
/usr/share/man/man1/gitk.*
|
||||
/usr/share/doc/git-core/gitk.*
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "commit.h"
|
||||
|
||||
static int show_root_diff = 0;
|
||||
static int no_commit_id = 0;
|
||||
static int verbose_header = 0;
|
||||
static int ignore_merges = 1;
|
||||
static int read_stdin = 0;
|
||||
@ -29,7 +30,8 @@ static int call_diff_flush(void)
|
||||
return 0;
|
||||
}
|
||||
if (header) {
|
||||
printf("%s%c", header, diff_options.line_termination);
|
||||
if (!no_commit_id)
|
||||
printf("%s%c", header, diff_options.line_termination);
|
||||
header = NULL;
|
||||
}
|
||||
diff_flush(&diff_options);
|
||||
@ -231,6 +233,10 @@ int main(int argc, const char **argv)
|
||||
show_root_diff = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--no-commit-id")) {
|
||||
no_commit_id = 1;
|
||||
continue;
|
||||
}
|
||||
usage(diff_tree_usage);
|
||||
}
|
||||
if (diff_options.output_format == DIFF_FORMAT_PATCH)
|
||||
|
31
git-am.sh
31
git-am.sh
@ -224,29 +224,33 @@ do
|
||||
git-stripspace < "$dotest/msg" > "$dotest/msg-clean"
|
||||
;;
|
||||
esac
|
||||
resume=
|
||||
|
||||
GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")"
|
||||
GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")"
|
||||
GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")"
|
||||
SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
|
||||
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
|
||||
|
||||
SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
|
||||
case "$keep_subject" in -k) SUBJECT="[PATCH] $SUBJECT" ;; esac
|
||||
if test '' != "$SIGNOFF"
|
||||
then
|
||||
|
||||
case "$resume" in
|
||||
'')
|
||||
if test '' != "$SIGNOFF"
|
||||
then
|
||||
LAST_SIGNED_OFF_BY=`
|
||||
sed -ne '/^Signed-off-by: /p' "$dotest/msg-clean" |
|
||||
tail -n 1
|
||||
sed -ne '/^Signed-off-by: /p' \
|
||||
"$dotest/msg-clean" |
|
||||
tail -n 1
|
||||
`
|
||||
ADD_SIGNOFF=$(test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
|
||||
ADD_SIGNOFF=`
|
||||
test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
|
||||
test '' = "$LAST_SIGNED_OFF_BY" && echo
|
||||
echo "$SIGNOFF"
|
||||
})
|
||||
else
|
||||
}`
|
||||
else
|
||||
ADD_SIGNOFF=
|
||||
fi
|
||||
{
|
||||
fi
|
||||
{
|
||||
echo "$SUBJECT"
|
||||
if test -s "$dotest/msg-clean"
|
||||
then
|
||||
@ -257,8 +261,11 @@ do
|
||||
then
|
||||
echo "$ADD_SIGNOFF"
|
||||
fi
|
||||
} >"$dotest/final-commit"
|
||||
} >"$dotest/final-commit"
|
||||
;;
|
||||
esac
|
||||
|
||||
resume=
|
||||
if test "$interactive" = t
|
||||
then
|
||||
test -t 0 ||
|
||||
|
@ -565,6 +565,11 @@ sub parselog {
|
||||
next if $t =~ m!\{arch\}/!;
|
||||
next if $t =~ m!\.arch-ids/!;
|
||||
next if $t =~ m!\.arch-inventory$!;
|
||||
# tla cat-archive-log will give us filenames with spaces as file\(sp)name - why?
|
||||
# we can assume that any filename with \ indicates some pika escaping that we want to get rid of.
|
||||
if ($t =~ /\\/ ){
|
||||
$t = `tla escape --unescaped '$t'`;
|
||||
}
|
||||
push (@tmp, shell_quote($t));
|
||||
}
|
||||
@$ref = @tmp;
|
||||
|
@ -102,6 +102,5 @@ rev=$(git-rev-parse --verify "$head") || exit
|
||||
git-check-ref-format "heads/$branchname" ||
|
||||
die "we do not like '$branchname' as a branch name."
|
||||
|
||||
leading=`expr "refs/heads/$branchname" : '\(.*\)/'` &&
|
||||
mkdir -p "$GIT_DIR/$leading" &&
|
||||
echo $rev > "$GIT_DIR/refs/heads/$branchname"
|
||||
git update-ref "refs/heads/$branchname" $rev
|
||||
|
||||
|
13
git-clone.sh
13
git-clone.sh
@ -9,7 +9,7 @@
|
||||
unset CDPATH
|
||||
|
||||
usage() {
|
||||
echo >&2 "* git clone [-l [-s]] [-q] [-u <upload-pack>] [-n] <repo> <dir>"
|
||||
echo >&2 "* git clone [-l [-s]] [-q] [-u <upload-pack>] [-n] <repo> [<dir>]"
|
||||
exit 1
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ fi
|
||||
|
||||
http_fetch () {
|
||||
# $1 = Remote, $2 = Local
|
||||
curl -nsf $curl_extra_args "$1" >"$2"
|
||||
curl -nsfL $curl_extra_args "$1" >"$2"
|
||||
}
|
||||
|
||||
clone_dumb_http () {
|
||||
@ -96,7 +96,10 @@ if base=$(get_repo_base "$repo"); then
|
||||
fi
|
||||
|
||||
dir="$2"
|
||||
mkdir "$dir" &&
|
||||
# Try using "humanish" part of source repo if user didn't specify one
|
||||
[ -z "$dir" ] && dir=$(echo "$repo" | sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*/||g')
|
||||
[ -e "$dir" ] && echo "$dir already exists." && usage
|
||||
mkdir -p "$dir" &&
|
||||
D=$(
|
||||
(cd "$dir" && git-init-db && pwd)
|
||||
) &&
|
||||
@ -163,7 +166,7 @@ yes,yes)
|
||||
rm -f "$D/.git/TMP_ALT"
|
||||
if test -f "$D/.git/TMP_ALT"
|
||||
then
|
||||
( cd $D &&
|
||||
( cd "$D" &&
|
||||
. git-parse-remote &&
|
||||
resolve_alternates "$repo" <"./.git/TMP_ALT" ) |
|
||||
while read alt
|
||||
@ -191,7 +194,7 @@ yes,yes)
|
||||
;;
|
||||
esac
|
||||
|
||||
cd $D || exit
|
||||
cd "$D" || exit
|
||||
|
||||
if test -f ".git/HEAD"
|
||||
then
|
||||
|
@ -184,7 +184,7 @@ then
|
||||
}
|
||||
'
|
||||
set_author_env=`git-cat-file commit "$use_commit" |
|
||||
sed -ne "$pick_author_script"`
|
||||
LANG=C LC_ALL=C sed -ne "$pick_author_script"`
|
||||
eval "$set_author_env"
|
||||
export GIT_AUTHOR_NAME
|
||||
export GIT_AUTHOR_EMAIL
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Pass --without docs to rpmbuild if you don't want the documetnation
|
||||
# Pass --without docs to rpmbuild if you don't want the documentation
|
||||
Name: git-core
|
||||
Version: @@VERSION@@
|
||||
Release: 1%{?dist}
|
||||
@ -7,9 +7,9 @@ License: GPL
|
||||
Group: Development/Tools
|
||||
URL: http://kernel.org/pub/software/scm/git/
|
||||
Source: http://kernel.org/pub/software/scm/git/%{name}-%{version}.tar.gz
|
||||
BuildRequires: zlib-devel, openssl-devel, curl-devel %{!?_without_docs:, xmlto, asciidoc > 6.0.3}
|
||||
BuildRequires: zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel %{!?_without_docs:, xmlto, asciidoc > 6.0.3}
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
Requires: zlib >= 1.2, rsync, rcs, curl, less, openssh-clients, python >= 2.3, tk >= 8.4
|
||||
Requires: zlib >= 1.2, rsync, rcs, curl, less, openssh-clients, python >= 2.3, expat
|
||||
|
||||
%description
|
||||
This is a stupid (but extremely fast) directory content manager. It
|
||||
@ -19,32 +19,113 @@ distributed source code management system. This package includes
|
||||
rudimentary tools that can be used as a SCM, but you should look
|
||||
elsewhere for tools for ordinary humans layered on top of this.
|
||||
|
||||
%package svn
|
||||
Summary: Git tools for importing Subversion repositories
|
||||
Group: Development/Tools
|
||||
Requires: git-core = %{version}-%{release}, subversion
|
||||
%description svn
|
||||
Git tools for importing Subversion repositories.
|
||||
|
||||
%package cvs
|
||||
Summary: Git tools for importing CVS repositories
|
||||
Group: Development/Tools
|
||||
Requires: git-core = %{version}-%{release}, cvs, cvsps
|
||||
%description cvs
|
||||
Git tools for importing CVS repositories.
|
||||
|
||||
%package arch
|
||||
Summary: Git tools for importing Arch repositories
|
||||
Group: Development/Tools
|
||||
Requires: git-core = %{version}-%{release}, tla
|
||||
%description arch
|
||||
Git tools for importing Arch repositories.
|
||||
|
||||
%package email
|
||||
Summary: Git tools for sending email
|
||||
Group: Development/Tools
|
||||
Requires: git-core = %{version}-%{release}
|
||||
%description email
|
||||
Git tools for sending email.
|
||||
|
||||
%package tk
|
||||
Summary: Git revision tree visualiser ('gitk')
|
||||
Group: Development/Tools
|
||||
Requires: git-core = %{version}-%{release}, tk >= 8.4
|
||||
%description tk
|
||||
Git revision tree visualiser ('gitk')
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" WITH_OWN_SUBPROCESS_PY=YesPlease \
|
||||
make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" WITH_OWN_SUBPROCESS_PY=YesPlease WITH_SEND_EMAIL=1 \
|
||||
prefix=%{_prefix} all %{!?_without_docs: doc}
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
make %{_smp_mflags} DESTDIR=$RPM_BUILD_ROOT WITH_OWN_SUBPROCESS_PY=YesPlease \
|
||||
make %{_smp_mflags} DESTDIR=$RPM_BUILD_ROOT WITH_OWN_SUBPROCESS_PY=YesPlease WITH_SEND_EMAIL=1 \
|
||||
prefix=%{_prefix} mandir=%{_mandir} \
|
||||
install %{!?_without_docs: install-doc}
|
||||
|
||||
(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "arch|svn|cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@) > bin-man-doc-files
|
||||
%if %{!?_without_docs:1}0
|
||||
(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "arch|svn|git-cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
|
||||
%endif
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
%files
|
||||
%files svn
|
||||
%defattr(-,root,root)
|
||||
%{_bindir}/*svn*
|
||||
%doc Documentation/*svn*.txt
|
||||
%{!?_without_docs: %{_mandir}/man1/*svn*.1*}
|
||||
%{!?_without_docs: %doc Documentation/*svn*.html }
|
||||
|
||||
%files cvs
|
||||
%defattr(-,root,root)
|
||||
%doc Documentation/*git-cvs*.txt
|
||||
%{_bindir}/*cvs*
|
||||
%{!?_without_docs: %{_mandir}/man1/*cvs*.1*}
|
||||
%{!?_without_docs: %doc Documentation/*git-cvs*.html }
|
||||
|
||||
%files arch
|
||||
%defattr(-,root,root)
|
||||
%doc Documentation/*arch*.txt
|
||||
%{_bindir}/*arch*
|
||||
%{!?_without_docs: %{_mandir}/man1/*arch*.1*}
|
||||
%{!?_without_docs: %doc Documentation/*arch*.html }
|
||||
|
||||
%files email
|
||||
%defattr(-,root,root)
|
||||
%doc Documentation/*email*.txt
|
||||
%{_bindir}/*email*
|
||||
%{!?_without_docs: %{_mandir}/man1/*email*.1*}
|
||||
%{!?_without_docs: %doc Documentation/*email*.html }
|
||||
|
||||
%files tk
|
||||
%defattr(-,root,root)
|
||||
%doc Documentation/*gitk*.txt
|
||||
%{_bindir}/*gitk*
|
||||
%{!?_without_docs: %{_mandir}/man1/*gitk*.1*}
|
||||
%{!?_without_docs: %doc Documentation/*gitk*.html }
|
||||
|
||||
%files -f bin-man-doc-files
|
||||
%defattr(-,root,root)
|
||||
%{_bindir}/*
|
||||
%{_datadir}/git-core/
|
||||
%doc README COPYING Documentation/*.txt
|
||||
%{!?_without_docs: %doc Documentation/*.html }
|
||||
%{!?_without_docs: %{_mandir}/man1/*.1*}
|
||||
%{!?_without_docs: %{_mandir}/man7/*.7*}
|
||||
|
||||
%changelog
|
||||
* Thu Nov 10 2005 Chris Wright <chrisw@osdl.org> 0.99.9g-1
|
||||
- zlib dependency fix
|
||||
- Minor cleanups from split
|
||||
- Move arch import to separate package as well
|
||||
|
||||
* Tue Sep 27 2005 Jim Radford <radford@blackbean.org>
|
||||
- Move programs with non-standard dependencies (svn, cvs, email)
|
||||
into separate packages
|
||||
|
||||
* Tue Sep 27 2005 H. Peter Anvin <hpa@zytor.com>
|
||||
- parallelize build
|
||||
- COPTS -> CFLAGS
|
||||
|
225
git-cvsexportcommit.perl
Executable file
225
git-cvsexportcommit.perl
Executable file
@ -0,0 +1,225 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
use strict;
|
||||
use Getopt::Std;
|
||||
use File::Temp qw(tempdir);
|
||||
use Data::Dumper;
|
||||
|
||||
unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
|
||||
die "GIT_DIR is not defined or is unreadable";
|
||||
}
|
||||
|
||||
our ($opt_h, $opt_p, $opt_v, $opt_c );
|
||||
|
||||
getopt('hpvc');
|
||||
|
||||
$opt_h && usage();
|
||||
|
||||
die "Need at least one commit identifier!" unless @ARGV;
|
||||
|
||||
# setup a tempdir
|
||||
our ($tmpdir, $tmpdirname) = tempdir('git-cvsapplycommit-XXXXXX',
|
||||
TMPDIR => 1,
|
||||
CLEANUP => 1);
|
||||
|
||||
print Dumper(@ARGV);
|
||||
# resolve target commit
|
||||
my $commit;
|
||||
$commit = pop @ARGV;
|
||||
$commit = `git-rev-parse --verify "$commit"^0`;
|
||||
chomp $commit;
|
||||
if ($?) {
|
||||
die "The commit reference $commit did not resolve!";
|
||||
}
|
||||
|
||||
# resolve what parent we want
|
||||
my $parent;
|
||||
if (@ARGV) {
|
||||
$parent = pop @ARGV;
|
||||
$parent = `git-rev-parse --verify "$parent"^0"`;
|
||||
chomp $parent;
|
||||
if ($?) {
|
||||
die "The parent reference did not resolve!";
|
||||
}
|
||||
}
|
||||
|
||||
# find parents from the commit itself
|
||||
my @commit = `git-cat-file commit $commit`;
|
||||
my @parents;
|
||||
foreach my $p (@commit) {
|
||||
if ($p =~ m/^$/) { # end of commit headers, we're done
|
||||
last;
|
||||
}
|
||||
if ($p =~ m/^parent (\w{40})$/) { # found a parent
|
||||
push @parents, $1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($parent) {
|
||||
# double check that it's a valid parent
|
||||
foreach my $p (@parents) {
|
||||
my $found;
|
||||
if ($p eq $parent) {
|
||||
$found = 1;
|
||||
last;
|
||||
}; # found it
|
||||
die "Did not find $parent in the parents for this commit!";
|
||||
}
|
||||
} else { # we don't have a parent from the cmdline...
|
||||
if (@parents == 1) { # it's safe to get it from the commit
|
||||
$parent = $parents[0];
|
||||
} else { # or perhaps not!
|
||||
die "This commit has more than one parent -- please name the parent you want to use explicitly";
|
||||
}
|
||||
}
|
||||
|
||||
$opt_v && print "Applying to CVS commit $commit from parent $parent\n";
|
||||
|
||||
# grab the commit message
|
||||
`git-cat-file commit $commit | sed -e '1,/^\$/d' > .msg`;
|
||||
$? && die "Error extraction the commit message";
|
||||
|
||||
my (@afiles, @dfiles, @mfiles);
|
||||
my @files = `git-diff-tree -r $parent $commit`;
|
||||
print @files;
|
||||
$? && die "Error in git-diff-tree";
|
||||
foreach my $f (@files) {
|
||||
chomp $f;
|
||||
my @fields = split(m/\s+/, $f);
|
||||
if ($fields[4] eq 'A') {
|
||||
push @afiles, $fields[5];
|
||||
}
|
||||
if ($fields[4] eq 'M') {
|
||||
push @mfiles, $fields[5];
|
||||
}
|
||||
if ($fields[4] eq 'R') {
|
||||
push @dfiles, $fields[5];
|
||||
}
|
||||
}
|
||||
$opt_v && print "The commit affects:\n ";
|
||||
$opt_v && print join ("\n ", @afiles,@mfiles,@dfiles) . "\n\n";
|
||||
undef @files; # don't need it anymore
|
||||
|
||||
# check that the files are clean and up to date according to cvs
|
||||
my $dirty;
|
||||
foreach my $f (@afiles, @mfiles, @dfiles) {
|
||||
# TODO:we need to handle removed in cvs and/or new (from git)
|
||||
my $status = `cvs -q status "$f" | grep '^File: '`;
|
||||
|
||||
unless ($status =~ m/Status: Up-to-date$/) {
|
||||
$dirty = 1;
|
||||
warn "File $f not up to date in your CVS checkout!\n";
|
||||
}
|
||||
}
|
||||
if ($dirty) {
|
||||
die "Exiting: your CVS tree is not clean for this merge.";
|
||||
}
|
||||
|
||||
###
|
||||
### NOTE: if you are planning to die() past this point
|
||||
### you MUST call cleanupcvs(@files) before die()
|
||||
###
|
||||
|
||||
|
||||
print "'Patching' binary files\n";
|
||||
|
||||
my @bfiles = `git-diff-tree -p $parent $commit | grep '^Binary'`;
|
||||
@bfiles = map { chomp } @bfiles;
|
||||
foreach my $f (@bfiles) {
|
||||
# check that the file in cvs matches the "old" file
|
||||
# extract the file to $tmpdir and comparre with cmp
|
||||
my $tree = `git-rev-parse $parent^{tree} `;
|
||||
chomp $tree;
|
||||
my $blob = `git-ls-tree $tree "$f" | cut -f 1 | cut -d ' ' -f 3`;
|
||||
chomp $blob;
|
||||
`git-cat-file blob $blob > $tmpdir/blob`;
|
||||
`cmp -q $f $tmpdir/blob`;
|
||||
if ($?) {
|
||||
warn "Binary file $f in CVS does not match parent.\n";
|
||||
$dirty = 1;
|
||||
next;
|
||||
}
|
||||
|
||||
# replace with the new file
|
||||
`git-cat-file blob $blob > $f`;
|
||||
|
||||
# TODO: something smart with file modes
|
||||
|
||||
}
|
||||
if ($dirty) {
|
||||
cleanupcvs(@files);
|
||||
die "Exiting: Binary files in CVS do not match parent";
|
||||
}
|
||||
|
||||
## apply non-binary changes
|
||||
my $fuzz = $opt_p ? 0 : 2;
|
||||
|
||||
print "Patching non-binary files\n";
|
||||
print `(git-diff-tree -p $parent -p $commit | patch -p1 -F $fuzz ) 2>&1`;
|
||||
|
||||
my $dirtypatch = 0;
|
||||
if (($? >> 8) == 2) {
|
||||
cleanupcvs(@files);
|
||||
die "Exiting: Patch reported serious trouble -- you will have to apply this patch manually";
|
||||
} elsif (($? >> 8) == 1) { # some hunks failed to apply
|
||||
$dirtypatch = 1;
|
||||
}
|
||||
|
||||
foreach my $f (@afiles) {
|
||||
`cvs add $f`;
|
||||
if ($?) {
|
||||
$dirty = 1;
|
||||
warn "Failed to cvs add $f -- you may need to do it manually";
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $f (@dfiles) {
|
||||
`cvs rm -f $f`;
|
||||
if ($?) {
|
||||
$dirty = 1;
|
||||
warn "Failed to cvs rm -f $f -- you may need to do it manually";
|
||||
}
|
||||
}
|
||||
|
||||
print "Commit to CVS\n";
|
||||
my $commitfiles = join(' ', @afiles, @mfiles, @dfiles);
|
||||
my $cmd = "cvs commit -F .msg $commitfiles";
|
||||
|
||||
if ($dirtypatch) {
|
||||
print "NOTE: One or more hunks failed to apply cleanly.\n";
|
||||
print "Resolve the conflicts and then commit using:n";
|
||||
print "\n $cmd\n\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
if ($opt_c) {
|
||||
print "Autocommit\n $cmd\n";
|
||||
print `cvs commit -F .msg $commitfiles 2>&1`;
|
||||
if ($?) {
|
||||
cleanupcvs(@files);
|
||||
die "Exiting: The commit did not succeed";
|
||||
}
|
||||
print "Committed successfully to CVS\n";
|
||||
} else {
|
||||
print "Ready for you to commit, just run:\n\n $cmd\n";
|
||||
}
|
||||
sub usage {
|
||||
print STDERR <<END;
|
||||
Usage: GIT_DIR=/path/to/.gi ${\basename $0} # fetch/update GIT from CVS
|
||||
[-h] [-p] [ parent ] commit
|
||||
END
|
||||
exit(1);
|
||||
}
|
||||
|
||||
# ensure cvs is clean before we die
|
||||
sub cleanupcvs {
|
||||
my @files = @_;
|
||||
foreach my $f (@files) {
|
||||
`cvs -q update -C "$f"`;
|
||||
if ($?) {
|
||||
warn "Warning! Failed to cleanup state of $f\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -230,7 +230,7 @@ do
|
||||
$u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
|
||||
print "$u";
|
||||
' "$remote_name")
|
||||
head=$(curl -nsf $curl_extra_args "$remote/$remote_name_quoted") &&
|
||||
head=$(curl -nsfL $curl_extra_args "$remote/$remote_name_quoted") &&
|
||||
expr "$head" : "$_x40\$" >/dev/null ||
|
||||
die "Failed to fetch $remote_name from $remote"
|
||||
echo >&2 Fetching "$remote_name from $remote" using http
|
||||
|
@ -8,6 +8,7 @@
|
||||
usage () {
|
||||
echo >&2 "usage: $0"' [-n] [-o dir | --stdout] [--keep-subject] [--mbox]
|
||||
[--check] [--signoff] [-<diff options>...]
|
||||
[--help]
|
||||
( from..to ... | upstream [ our-head ] )
|
||||
|
||||
Prepare each commit with its patch since our-head forked from upstream,
|
||||
@ -63,6 +64,9 @@ do
|
||||
--output-directo|--output-director|--output-directory)
|
||||
case "$#" in 1) usage ;; esac; shift
|
||||
outdir="$1" ;;
|
||||
-h|--h|--he|--hel|--help)
|
||||
usage
|
||||
;;
|
||||
-*' '* | -*"$LF"* | -*' '*)
|
||||
# Ignore diff option that has whitespace for now.
|
||||
;;
|
||||
@ -197,7 +201,7 @@ process_one () {
|
||||
;;
|
||||
esac
|
||||
|
||||
eval "$(sed -ne "$whosepatchScript" $commsg)"
|
||||
eval "$(LANG=C LC_ALL=C sed -ne "$whosepatchScript" $commsg)"
|
||||
test "$author,$au" = ",$me" || {
|
||||
mailScript="$mailScript"'
|
||||
a\
|
||||
|
23
git-lost-found.sh
Executable file
23
git-lost-found.sh
Executable file
@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
|
||||
. git-sh-setup || die "Not a git archive."
|
||||
|
||||
laf="$GIT_DIR/lost-found"
|
||||
rm -fr "$laf" && mkdir -p "$laf/commit" "$laf/other" || exit
|
||||
|
||||
git fsck-objects |
|
||||
while read dangling type sha1
|
||||
do
|
||||
case "$dangling" in
|
||||
dangling)
|
||||
if git-rev-parse --verify "$sha1^0" >/dev/null 2>/dev/null
|
||||
then
|
||||
dir="$laf/commit"
|
||||
git-show-branch "$sha1"
|
||||
else
|
||||
dir="$laf/other"
|
||||
fi
|
||||
echo "$sha1" >"$dir/$sha1"
|
||||
;;
|
||||
esac
|
||||
done
|
@ -5,6 +5,9 @@
|
||||
# Resolve two or more trees.
|
||||
#
|
||||
|
||||
LF='
|
||||
'
|
||||
|
||||
# The first parameters up to -- are merge bases; the rest are heads.
|
||||
bases= head= remotes= sep_seen=
|
||||
for arg
|
||||
@ -42,14 +45,18 @@ CNT=1 ;# counting our head
|
||||
NON_FF_MERGE=0
|
||||
for SHA1 in $remotes
|
||||
do
|
||||
common=$(git-merge-base $MRC $SHA1) ||
|
||||
common=$(git-merge-base --all $MRC $SHA1) ||
|
||||
die "Unable to find common commit with $SHA1"
|
||||
|
||||
if test "$common" = $SHA1
|
||||
then
|
||||
case "$common" in
|
||||
?*"$LF"?*)
|
||||
die "Not trivially mergeable."
|
||||
;;
|
||||
$SHA1)
|
||||
echo "Already up-to-date with $SHA1"
|
||||
continue
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
CNT=`expr $CNT + 1`
|
||||
PARENT="$PARENT -p $SHA1"
|
||||
@ -79,7 +86,15 @@ do
|
||||
exit 2 ; # Automatic merge failed; should not be doing Octopus
|
||||
next=$(git-write-tree 2>/dev/null)
|
||||
fi
|
||||
MRC=$common
|
||||
|
||||
# We have merged the other branch successfully. Ideally
|
||||
# we could implement OR'ed heads in merge-base, and keep
|
||||
# a list of commits we have merged so far in MRC to feed
|
||||
# them to merge-base, but we approximate it by keep using
|
||||
# the current MRC. We used to update it to $common, which
|
||||
# was incorrectly doing AND'ed merge-base here, which was
|
||||
# unneeded.
|
||||
|
||||
MRT=$next
|
||||
done
|
||||
|
||||
|
@ -40,7 +40,7 @@ case "${1:-.}${2:-.}${3:-.}" in
|
||||
;;
|
||||
|
||||
#
|
||||
# Added in both (check for same permissions).
|
||||
# Added in both, identically (check for same permissions).
|
||||
#
|
||||
".$3$2")
|
||||
if [ "$6" != "$7" ]; then
|
||||
@ -56,10 +56,27 @@ case "${1:-.}${2:-.}${3:-.}" in
|
||||
#
|
||||
# Modified in both, but differently.
|
||||
#
|
||||
"$1$2$3")
|
||||
echo "Auto-merging $4."
|
||||
orig=`git-unpack-file $1`
|
||||
"$1$2$3" | ".$2$3")
|
||||
src2=`git-unpack-file $3`
|
||||
case "$1" in
|
||||
'')
|
||||
echo "Added $4 in both, but differently."
|
||||
# This extracts OUR file in $orig, and uses git-apply to
|
||||
# remove lines that are unique to ours.
|
||||
orig=`git-unpack-file $2`
|
||||
sz0=`wc -c <"$orig"`
|
||||
diff -u -La/$orig -Lb/$orig $orig $src2 | git-apply --no-add
|
||||
sz1=`wc -c <"$orig"`
|
||||
|
||||
# If we do not have enough common material, it is not
|
||||
# worth trying two-file merge using common subsections.
|
||||
expr "$sz0" \< "$sz1" \* 2 >/dev/null || : >$orig
|
||||
;;
|
||||
*)
|
||||
echo "Auto-merging $4."
|
||||
orig=`git-unpack-file $1`
|
||||
;;
|
||||
esac
|
||||
|
||||
# We reset the index to the first branch, making
|
||||
# git-diff-file useful
|
||||
@ -73,6 +90,9 @@ case "${1:-.}${2:-.}${3:-.}" in
|
||||
echo "ERROR: Permissions conflict: $5->$6,$7."
|
||||
ret=1
|
||||
fi
|
||||
if [ "$1" = '' ]; then
|
||||
ret=1
|
||||
fi
|
||||
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "ERROR: Merge conflict in $4."
|
||||
|
@ -1,4 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (C) 2005 Fredrik Kuivinen
|
||||
#
|
||||
|
||||
import sys, math, random, os, re, signal, tempfile, stat, errno, traceback
|
||||
from heapq import heappush, heappop
|
||||
@ -7,6 +10,11 @@ from sets import Set
|
||||
sys.path.append('''@@GIT_PYTHON_PATH@@''')
|
||||
from gitMergeCommon import *
|
||||
|
||||
outputIndent = 0
|
||||
def output(*args):
|
||||
sys.stdout.write(' '*outputIndent)
|
||||
printList(args)
|
||||
|
||||
originalIndexFile = os.environ.get('GIT_INDEX_FILE',
|
||||
os.environ.get('GIT_DIR', '.git') + '/index')
|
||||
temporaryIndexFile = os.environ.get('GIT_DIR', '.git') + \
|
||||
@ -41,27 +49,27 @@ def merge(h1, h2, branch1Name, branch2Name, graph, callDepth=0):
|
||||
assert(isinstance(h1, Commit) and isinstance(h2, Commit))
|
||||
assert(isinstance(graph, Graph))
|
||||
|
||||
def infoMsg(*args):
|
||||
sys.stdout.write(' '*callDepth)
|
||||
printList(args)
|
||||
global outputIndent
|
||||
|
||||
infoMsg('Merging:')
|
||||
infoMsg(h1)
|
||||
infoMsg(h2)
|
||||
output('Merging:')
|
||||
output(h1)
|
||||
output(h2)
|
||||
sys.stdout.flush()
|
||||
|
||||
ca = getCommonAncestors(graph, h1, h2)
|
||||
infoMsg('found', len(ca), 'common ancestor(s):')
|
||||
output('found', len(ca), 'common ancestor(s):')
|
||||
for x in ca:
|
||||
infoMsg(x)
|
||||
output(x)
|
||||
sys.stdout.flush()
|
||||
|
||||
mergedCA = ca[0]
|
||||
for h in ca[1:]:
|
||||
outputIndent = callDepth+1
|
||||
[mergedCA, dummy] = merge(mergedCA, h,
|
||||
'Temporary shared merge branch 1',
|
||||
'Temporary shared merge branch 2',
|
||||
'Temporary merge branch 1',
|
||||
'Temporary merge branch 2',
|
||||
graph, callDepth+1)
|
||||
outputIndent = callDepth
|
||||
assert(isinstance(mergedCA, Commit))
|
||||
|
||||
global cacheOnly
|
||||
@ -116,7 +124,7 @@ def mergeTrees(head, merge, common, branch1Name, branch2Name):
|
||||
assert(isSha(head) and isSha(merge) and isSha(common))
|
||||
|
||||
if common == merge:
|
||||
print 'Already uptodate!'
|
||||
output('Already uptodate!')
|
||||
return [head, True]
|
||||
|
||||
if cacheOnly:
|
||||
@ -207,7 +215,7 @@ def mergeFile(oPath, oSha, oMode, aPath, aSha, aMode, bPath, bSha, bMode,
|
||||
os.unlink(orig)
|
||||
os.unlink(src1)
|
||||
os.unlink(src2)
|
||||
|
||||
|
||||
clean = (code == 0)
|
||||
else:
|
||||
assert(stat.S_ISLNK(aMode) and stat.S_ISLNK(bMode))
|
||||
@ -295,13 +303,14 @@ def uniquePath(path, branch):
|
||||
else:
|
||||
raise
|
||||
|
||||
newPath = path + '_' + branch
|
||||
branch = branch.replace('/', '_')
|
||||
newPath = path + '~' + branch
|
||||
suffix = 0
|
||||
while newPath in currentFileSet or \
|
||||
newPath in currentDirectorySet or \
|
||||
fileExists(newPath):
|
||||
suffix += 1
|
||||
newPath = path + '_' + branch + '_' + str(suffix)
|
||||
newPath = path + '~' + branch + '_' + str(suffix)
|
||||
currentFileSet.add(newPath)
|
||||
return newPath
|
||||
|
||||
@ -553,23 +562,24 @@ def processRenames(renamesA, renamesB, branchNameA, branchNameB):
|
||||
ren2.processed = True
|
||||
|
||||
if ren1.dstName != ren2.dstName:
|
||||
print 'CONFLICT (rename/rename): Rename', \
|
||||
fmtRename(path, ren1.dstName), 'in branch', branchName1, \
|
||||
'rename', fmtRename(path, ren2.dstName), 'in', branchName2
|
||||
output('CONFLICT (rename/rename): Rename',
|
||||
fmtRename(path, ren1.dstName), 'in branch', branchName1,
|
||||
'rename', fmtRename(path, ren2.dstName), 'in',
|
||||
branchName2)
|
||||
cleanMerge = False
|
||||
|
||||
if ren1.dstName in currentDirectorySet:
|
||||
dstName1 = uniquePath(ren1.dstName, branchName1)
|
||||
print ren1.dstName, 'is a directory in', branchName2, \
|
||||
'adding as', dstName1, 'instead.'
|
||||
output(ren1.dstName, 'is a directory in', branchName2,
|
||||
'adding as', dstName1, 'instead.')
|
||||
removeFile(False, ren1.dstName)
|
||||
else:
|
||||
dstName1 = ren1.dstName
|
||||
|
||||
if ren2.dstName in currentDirectorySet:
|
||||
dstName2 = uniquePath(ren2.dstName, branchName2)
|
||||
print ren2.dstName, 'is a directory in', branchName1, \
|
||||
'adding as', dstName2, 'instead.'
|
||||
output(ren2.dstName, 'is a directory in', branchName1,
|
||||
'adding as', dstName2, 'instead.')
|
||||
removeFile(False, ren2.dstName)
|
||||
else:
|
||||
dstName2 = ren1.dstName
|
||||
@ -577,18 +587,21 @@ def processRenames(renamesA, renamesB, branchNameA, branchNameB):
|
||||
updateFile(False, ren1.dstSha, ren1.dstMode, dstName1)
|
||||
updateFile(False, ren2.dstSha, ren2.dstMode, dstName2)
|
||||
else:
|
||||
print 'Renaming', fmtRename(path, ren1.dstName)
|
||||
[resSha, resMode, clean, merge] = \
|
||||
mergeFile(ren1.srcName, ren1.srcSha, ren1.srcMode,
|
||||
ren1.dstName, ren1.dstSha, ren1.dstMode,
|
||||
ren2.dstName, ren2.dstSha, ren2.dstMode,
|
||||
branchName1, branchName2)
|
||||
|
||||
if merge or not clean:
|
||||
output('Renaming', fmtRename(path, ren1.dstName))
|
||||
|
||||
if merge:
|
||||
print 'Auto-merging', ren1.dstName
|
||||
output('Auto-merging', ren1.dstName)
|
||||
|
||||
if not clean:
|
||||
print 'CONFLICT (content): merge conflict in', ren1.dstName
|
||||
output('CONFLICT (content): merge conflict in',
|
||||
ren1.dstName)
|
||||
cleanMerge = False
|
||||
|
||||
if not cacheOnly:
|
||||
@ -612,25 +625,25 @@ def processRenames(renamesA, renamesB, branchNameA, branchNameB):
|
||||
|
||||
if ren1.dstName in currentDirectorySet:
|
||||
newPath = uniquePath(ren1.dstName, branchName1)
|
||||
print 'CONFLICT (rename/directory): Rename', \
|
||||
fmtRename(ren1.srcName, ren1.dstName), 'in', branchName1,\
|
||||
'directory', ren1.dstName, 'added in', branchName2
|
||||
print 'Renaming', ren1.srcName, 'to', newPath, 'instead'
|
||||
output('CONFLICT (rename/directory): Rename',
|
||||
fmtRename(ren1.srcName, ren1.dstName), 'in', branchName1,
|
||||
'directory', ren1.dstName, 'added in', branchName2)
|
||||
output('Renaming', ren1.srcName, 'to', newPath, 'instead')
|
||||
cleanMerge = False
|
||||
removeFile(False, ren1.dstName)
|
||||
updateFile(False, ren1.dstSha, ren1.dstMode, newPath)
|
||||
elif srcShaOtherBranch == None:
|
||||
print 'CONFLICT (rename/delete): Rename', \
|
||||
fmtRename(ren1.srcName, ren1.dstName), 'in', \
|
||||
branchName1, 'and deleted in', branchName2
|
||||
output('CONFLICT (rename/delete): Rename',
|
||||
fmtRename(ren1.srcName, ren1.dstName), 'in',
|
||||
branchName1, 'and deleted in', branchName2)
|
||||
cleanMerge = False
|
||||
updateFile(False, ren1.dstSha, ren1.dstMode, ren1.dstName)
|
||||
elif dstShaOtherBranch:
|
||||
newPath = uniquePath(ren1.dstName, branchName2)
|
||||
print 'CONFLICT (rename/add): Rename', \
|
||||
fmtRename(ren1.srcName, ren1.dstName), 'in', \
|
||||
branchName1 + '.', ren1.dstName, 'added in', branchName2
|
||||
print 'Adding as', newPath, 'instead'
|
||||
output('CONFLICT (rename/add): Rename',
|
||||
fmtRename(ren1.srcName, ren1.dstName), 'in',
|
||||
branchName1 + '.', ren1.dstName, 'added in', branchName2)
|
||||
output('Adding as', newPath, 'instead')
|
||||
updateFile(False, dstShaOtherBranch, dstModeOtherBranch, newPath)
|
||||
cleanMerge = False
|
||||
tryMerge = True
|
||||
@ -638,12 +651,12 @@ def processRenames(renamesA, renamesB, branchNameA, branchNameB):
|
||||
dst2 = renames2.getDst(ren1.dstName)
|
||||
newPath1 = uniquePath(ren1.dstName, branchName1)
|
||||
newPath2 = uniquePath(dst2.dstName, branchName2)
|
||||
print 'CONFLICT (rename/rename): Rename', \
|
||||
fmtRename(ren1.srcName, ren1.dstName), 'in', \
|
||||
branchName1+'. Rename', \
|
||||
fmtRename(dst2.srcName, dst2.dstName), 'in', branchName2
|
||||
print 'Renaming', ren1.srcName, 'to', newPath1, 'and', \
|
||||
dst2.srcName, 'to', newPath2, 'instead'
|
||||
output('CONFLICT (rename/rename): Rename',
|
||||
fmtRename(ren1.srcName, ren1.dstName), 'in',
|
||||
branchName1+'. Rename',
|
||||
fmtRename(dst2.srcName, dst2.dstName), 'in', branchName2)
|
||||
output('Renaming', ren1.srcName, 'to', newPath1, 'and',
|
||||
dst2.srcName, 'to', newPath2, 'instead')
|
||||
removeFile(False, ren1.dstName)
|
||||
updateFile(False, ren1.dstSha, ren1.dstMode, newPath1)
|
||||
updateFile(False, dst2.dstSha, dst2.dstMode, newPath2)
|
||||
@ -653,18 +666,21 @@ def processRenames(renamesA, renamesB, branchNameA, branchNameB):
|
||||
tryMerge = True
|
||||
|
||||
if tryMerge:
|
||||
print 'Renaming', fmtRename(ren1.srcName, ren1.dstName)
|
||||
[resSha, resMode, clean, merge] = \
|
||||
mergeFile(ren1.srcName, ren1.srcSha, ren1.srcMode,
|
||||
ren1.dstName, ren1.dstSha, ren1.dstMode,
|
||||
ren1.srcName, srcShaOtherBranch, srcModeOtherBranch,
|
||||
branchName1, branchName2)
|
||||
|
||||
if merge or not clean:
|
||||
output('Renaming', fmtRename(ren1.srcName, ren1.dstName))
|
||||
|
||||
if merge:
|
||||
print 'Auto-merging', ren1.dstName
|
||||
output('Auto-merging', ren1.dstName)
|
||||
|
||||
if not clean:
|
||||
print 'CONFLICT (rename/modify): Merge conflict in', ren1.dstName
|
||||
output('CONFLICT (rename/modify): Merge conflict in',
|
||||
ren1.dstName)
|
||||
cleanMerge = False
|
||||
|
||||
if not cacheOnly:
|
||||
@ -709,21 +725,21 @@ def processEntry(entry, branch1Name, branch2Name):
|
||||
(not aSha and bSha == oSha):
|
||||
# Deleted in both or deleted in one and unchanged in the other
|
||||
if aSha:
|
||||
print 'Removing', path
|
||||
output('Removing', path)
|
||||
removeFile(True, path)
|
||||
else:
|
||||
# Deleted in one and changed in the other
|
||||
cleanMerge = False
|
||||
if not aSha:
|
||||
print 'CONFLICT (delete/modify):', path, 'deleted in', \
|
||||
branch1Name, 'and modified in', branch2Name + '.', \
|
||||
'Version', branch2Name, 'of', path, 'left in tree.'
|
||||
output('CONFLICT (delete/modify):', path, 'deleted in',
|
||||
branch1Name, 'and modified in', branch2Name + '.',
|
||||
'Version', branch2Name, 'of', path, 'left in tree.')
|
||||
mode = bMode
|
||||
sha = bSha
|
||||
else:
|
||||
print 'CONFLICT (modify/delete):', path, 'deleted in', \
|
||||
branch2Name, 'and modified in', branch1Name + '.', \
|
||||
'Version', branch1Name, 'of', path, 'left in tree.'
|
||||
output('CONFLICT (modify/delete):', path, 'deleted in',
|
||||
branch2Name, 'and modified in', branch1Name + '.',
|
||||
'Version', branch1Name, 'of', path, 'left in tree.')
|
||||
mode = aMode
|
||||
sha = aSha
|
||||
|
||||
@ -750,14 +766,14 @@ def processEntry(entry, branch1Name, branch2Name):
|
||||
if path in currentDirectorySet:
|
||||
cleanMerge = False
|
||||
newPath = uniquePath(path, addBranch)
|
||||
print 'CONFLICT (' + conf + '):', \
|
||||
'There is a directory with name', path, 'in', \
|
||||
otherBranch + '. Adding', path, 'as', newPath
|
||||
output('CONFLICT (' + conf + '):',
|
||||
'There is a directory with name', path, 'in',
|
||||
otherBranch + '. Adding', path, 'as', newPath)
|
||||
|
||||
removeFile(False, path)
|
||||
updateFile(False, sha, mode, newPath)
|
||||
else:
|
||||
print 'Adding', path
|
||||
output('Adding', path)
|
||||
updateFile(True, sha, mode, path)
|
||||
|
||||
elif not oSha and aSha and bSha:
|
||||
@ -767,10 +783,10 @@ def processEntry(entry, branch1Name, branch2Name):
|
||||
if aSha == bSha:
|
||||
if aMode != bMode:
|
||||
cleanMerge = False
|
||||
print 'CONFLICT: File', path, \
|
||||
'added identically in both branches, but permissions', \
|
||||
'conflict', '0%o' % aMode, '->', '0%o' % bMode
|
||||
print 'CONFLICT: adding with permission:', '0%o' % aMode
|
||||
output('CONFLICT: File', path,
|
||||
'added identically in both branches, but permissions',
|
||||
'conflict', '0%o' % aMode, '->', '0%o' % bMode)
|
||||
output('CONFLICT: adding with permission:', '0%o' % aMode)
|
||||
|
||||
updateFile(False, aSha, aMode, path)
|
||||
else:
|
||||
@ -780,9 +796,9 @@ def processEntry(entry, branch1Name, branch2Name):
|
||||
cleanMerge = False
|
||||
newPath1 = uniquePath(path, branch1Name)
|
||||
newPath2 = uniquePath(path, branch2Name)
|
||||
print 'CONFLICT (add/add): File', path, \
|
||||
'added non-identically in both branches. Adding as', \
|
||||
newPath1, 'and', newPath2, 'instead.'
|
||||
output('CONFLICT (add/add): File', path,
|
||||
'added non-identically in both branches. Adding as',
|
||||
newPath1, 'and', newPath2, 'instead.')
|
||||
removeFile(False, path)
|
||||
updateFile(False, aSha, aMode, newPath1)
|
||||
updateFile(False, bSha, bMode, newPath2)
|
||||
@ -791,7 +807,7 @@ def processEntry(entry, branch1Name, branch2Name):
|
||||
#
|
||||
# case D: Modified in both, but differently.
|
||||
#
|
||||
print 'Auto-merging', path
|
||||
output('Auto-merging', path)
|
||||
[sha, mode, clean, dummy] = \
|
||||
mergeFile(path, oSha, oMode,
|
||||
path, aSha, aMode,
|
||||
@ -801,7 +817,7 @@ def processEntry(entry, branch1Name, branch2Name):
|
||||
updateFile(True, sha, mode, path)
|
||||
else:
|
||||
cleanMerge = False
|
||||
print 'CONFLICT (content): Merge conflict in', path
|
||||
output('CONFLICT (content): Merge conflict in', path)
|
||||
|
||||
if cacheOnly:
|
||||
updateFile(False, sha, mode, path)
|
||||
|
11
git-merge.sh
11
git-merge.sh
@ -110,7 +110,14 @@ do
|
||||
die "$remote - not something we can merge"
|
||||
done
|
||||
|
||||
common=$(git-show-branch --merge-base $head "$@")
|
||||
case "$#" in
|
||||
1)
|
||||
common=$(git-merge-base --all $head "$@")
|
||||
;;
|
||||
*)
|
||||
common=$(git-show-branch --merge-base $head "$@")
|
||||
;;
|
||||
esac
|
||||
echo "$head" >"$GIT_DIR/ORIG_HEAD"
|
||||
|
||||
case "$#,$common,$no_commit" in
|
||||
@ -162,7 +169,7 @@ case "$#,$common,$no_commit" in
|
||||
up_to_date=t
|
||||
for remote
|
||||
do
|
||||
common_one=$(git-merge-base $head $remote)
|
||||
common_one=$(git-merge-base --all $head $remote)
|
||||
if test "$common_one" != "$remote"
|
||||
then
|
||||
up_to_date=f
|
||||
|
11
git-prune.sh
11
git-prune.sh
@ -27,3 +27,14 @@ sed -ne '/unreachable /{
|
||||
}
|
||||
|
||||
git-prune-packed $dryrun
|
||||
|
||||
redundant=$(git-pack-redundant --all)
|
||||
if test "" != "$redundant"
|
||||
then
|
||||
if test "" = $dryrun
|
||||
then
|
||||
echo "$redundant" | xargs rm -f
|
||||
else
|
||||
echo rm -f "$redundant"
|
||||
fi
|
||||
fi
|
||||
|
28
git-pull.sh
28
git-pull.sh
@ -7,7 +7,14 @@
|
||||
. git-sh-setup || die "Not a git archive"
|
||||
|
||||
usage () {
|
||||
die "git pull [-n] [--no-commit] [-s strategy]... <repo> <head>..."
|
||||
echo >&2 "usage: $0"' [-n] [--no-commit] [--no-summary] [--help]
|
||||
[-s strategy]...
|
||||
[<fetch-options>]
|
||||
<repo> <head>...
|
||||
|
||||
Fetch one or more remote refs and merge it/them into the current HEAD.
|
||||
'
|
||||
exit 1
|
||||
}
|
||||
|
||||
strategy_args= no_summary= no_commit=
|
||||
@ -33,6 +40,9 @@ do
|
||||
esac
|
||||
strategy_args="${strategy_args}-s $strategy "
|
||||
;;
|
||||
-h|--h|--he|--hel|--help)
|
||||
usage
|
||||
;;
|
||||
-*)
|
||||
# Pass thru anything that is meant for fetch.
|
||||
break
|
||||
@ -69,10 +79,22 @@ case "$merge_head" in
|
||||
exit 0
|
||||
;;
|
||||
?*' '?*)
|
||||
strategy_default_args='-s octopus'
|
||||
var=`git-var -l | sed -ne 's/^pull\.octopus=/-s /p'`
|
||||
if test '' = "$var"
|
||||
then
|
||||
strategy_default_args='-s octopus'
|
||||
else
|
||||
strategy_default_args=$var
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
strategy_default_args='-s resolve'
|
||||
var=`git-var -l | sed -ne 's/^pull\.twohead=/-s /p'`
|
||||
if test '' = "$var"
|
||||
then
|
||||
strategy_default_args='-s recursive'
|
||||
else
|
||||
strategy_default_args=$var
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
|
@ -46,7 +46,7 @@ esac
|
||||
shift
|
||||
|
||||
case "$remote" in
|
||||
http://* | https://* | git://*)
|
||||
git://*)
|
||||
die "Cannot use READ-ONLY transport to push to $remote" ;;
|
||||
rsync://*)
|
||||
die "Pushing with rsync transport is deprecated" ;;
|
||||
@ -57,4 +57,9 @@ test "$has_all" && set x "$has_all" "$@" && shift
|
||||
test "$has_force" && set x "$has_force" "$@" && shift
|
||||
test "$has_exec" && set x "$has_exec" "$@" && shift
|
||||
|
||||
exec git-send-pack "$@"
|
||||
case "$remote" in
|
||||
http://* | https://*)
|
||||
exec git-http-push "$@";;
|
||||
*)
|
||||
exec git-send-pack "$@";;
|
||||
esac
|
||||
|
@ -1,70 +0,0 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# Copyright 2005, Ryan Anderson <ryan@michonline.com>
|
||||
#
|
||||
# This file is licensed under the GPL v2, or a later version
|
||||
# at the discretion of Linus Torvalds.
|
||||
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
sub usage($);
|
||||
|
||||
# Sanity checks:
|
||||
my $GIT_DIR = $ENV{'GIT_DIR'} || ".git";
|
||||
|
||||
unless ( -d $GIT_DIR && -d $GIT_DIR . "/objects" &&
|
||||
-d $GIT_DIR . "/objects/" && -d $GIT_DIR . "/refs") {
|
||||
usage("Git repository not found.");
|
||||
}
|
||||
|
||||
usage("") if scalar @ARGV != 2;
|
||||
|
||||
my ($src,$dst) = @ARGV;
|
||||
|
||||
unless (-f $src || -l $src || -d $src) {
|
||||
usage("git rename: bad source '$src'");
|
||||
}
|
||||
|
||||
if (-e $dst) {
|
||||
usage("git rename: destinations '$dst' already exists");
|
||||
}
|
||||
|
||||
my (@allfiles,@srcfiles,@dstfiles);
|
||||
|
||||
$/ = "\0";
|
||||
open(F,"-|","git-ls-files","-z")
|
||||
or die "Failed to open pipe from git-ls-files: " . $!;
|
||||
|
||||
@allfiles = map { chomp; $_; } <F>;
|
||||
close(F);
|
||||
|
||||
my $safesrc = quotemeta($src);
|
||||
@srcfiles = grep /^$safesrc/, @allfiles;
|
||||
@dstfiles = @srcfiles;
|
||||
s#^$safesrc(/|$)#$dst$1# for @dstfiles;
|
||||
|
||||
rename($src,$dst)
|
||||
or die "rename failed: $!";
|
||||
|
||||
my $rc = system("git-update-index","--add","--",@dstfiles);
|
||||
die "git-update-index failed to add new name with code $?\n" if $rc;
|
||||
|
||||
$rc = system("git-update-index","--remove","--",@srcfiles);
|
||||
die "git-update-index failed to remove old name with code $?\n" if $rc;
|
||||
|
||||
|
||||
sub usage($) {
|
||||
my $s = shift;
|
||||
print $s, "\n" if (length $s != 0);
|
||||
print <<EOT;
|
||||
$0 <source> <dest>
|
||||
source must exist and be either a file, symlink or directory.
|
||||
dest must NOT exist.
|
||||
|
||||
Renames source to dest, and updates the git cache to reflect the change.
|
||||
Use "git commit" to make record the change permanently.
|
||||
EOT
|
||||
exit(1);
|
||||
}
|
@ -32,10 +32,6 @@ case ",$all_into_one," in
|
||||
rev_list=
|
||||
rev_parse='--all'
|
||||
pack_objects=
|
||||
# This part is a stop-gap until we have proper pack redundancy
|
||||
# checker.
|
||||
existing=`cd "$PACKDIR" && \
|
||||
find . -type f \( -name '*.pack' -o -name '*.idx' \) -print`
|
||||
;;
|
||||
esac
|
||||
if [ "$local" ]; then
|
||||
@ -46,6 +42,14 @@ name=$(git-rev-list --objects $rev_list $(git-rev-parse $rev_parse) |
|
||||
exit 1
|
||||
if [ -z "$name" ]; then
|
||||
echo Nothing new to pack.
|
||||
if test "$remove_redandant" = t ; then
|
||||
echo "Removing redundant packs."
|
||||
sync
|
||||
redundant=$(git-pack-redundant --all)
|
||||
if test "$redundant" != "" ; then
|
||||
echo $redundant | xargs rm
|
||||
fi
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
echo "Pack pack-$name created."
|
||||
@ -58,20 +62,10 @@ exit
|
||||
|
||||
if test "$remove_redandant" = t
|
||||
then
|
||||
# We know $existing are all redandant only when
|
||||
# all-into-one is used.
|
||||
if test "$all_into_one" != '' && test "$existing" != ''
|
||||
then
|
||||
sync
|
||||
( cd "$PACKDIR" &&
|
||||
for e in $existing
|
||||
do
|
||||
case "$e" in
|
||||
./pack-$name.pack | ./pack-$name.idx) ;;
|
||||
*) rm -f $e ;;
|
||||
esac
|
||||
done
|
||||
)
|
||||
sync
|
||||
redundant=$(git-pack-redundant --all)
|
||||
if test "$redundant" != "" ; then
|
||||
echo $redundant | xargs rm
|
||||
fi
|
||||
fi
|
||||
|
||||
|
@ -112,7 +112,7 @@ cherry-pick)
|
||||
q
|
||||
}'
|
||||
set_author_env=`git-cat-file commit "$commit" |
|
||||
sed -ne "$pick_author_script"`
|
||||
LANG=C LC_ALL=C sed -ne "$pick_author_script"`
|
||||
eval "$set_author_env"
|
||||
export GIT_AUTHOR_NAME
|
||||
export GIT_AUTHOR_EMAIL
|
||||
|
@ -25,7 +25,7 @@ use IPC::Open2;
|
||||
use SVN::Core;
|
||||
use SVN::Ra;
|
||||
|
||||
die "Need CVN:Core 1.2.1 or better" if $SVN::Core::VERSION lt "1.2.1";
|
||||
die "Need SVN:Core 1.2.1 or better" if $SVN::Core::VERSION lt "1.2.1";
|
||||
|
||||
$SIG{'PIPE'}="IGNORE";
|
||||
$ENV{'TZ'}="UTC";
|
||||
@ -34,7 +34,7 @@ our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T,$opt_b
|
||||
|
||||
sub usage() {
|
||||
print STDERR <<END;
|
||||
Usage: ${\basename $0} # fetch/update GIT from CVS
|
||||
Usage: ${\basename $0} # fetch/update GIT from SVN
|
||||
[-o branch-for-HEAD] [-h] [-v] [-l max_num_changes]
|
||||
[-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname]
|
||||
[-d|-D] [-i] [-u] [-s start_chg] [-m] [-M regex] [SVN_URL]
|
||||
@ -53,7 +53,6 @@ my $branch_name = $opt_b || "branches";
|
||||
|
||||
$opt_o ||= "origin";
|
||||
$opt_s ||= 1;
|
||||
$opt_l = 100 unless defined $opt_l;
|
||||
my $git_tree = $opt_C;
|
||||
$git_tree ||= ".";
|
||||
|
||||
@ -112,7 +111,9 @@ sub file {
|
||||
DIR => File::Spec->tmpdir(), UNLINK => 1);
|
||||
|
||||
print "... $rev $path ...\n" if $opt_v;
|
||||
eval { $self->{'svn'}->get_file($path,$rev,$fh); };
|
||||
my $pool = SVN::Pool->new();
|
||||
eval { $self->{'svn'}->get_file($path,$rev,$fh,$pool); };
|
||||
$pool->clear;
|
||||
if($@) {
|
||||
return undef if $@ =~ /Attempted to get checksum/;
|
||||
die $@;
|
||||
@ -258,10 +259,17 @@ EOM
|
||||
|
||||
open BRANCHES,">>", "$git_dir/svn2git";
|
||||
|
||||
sub get_file($$$) {
|
||||
my($rev,$branch,$path) = @_;
|
||||
sub node_kind($$$) {
|
||||
my ($branch, $path, $revision) = @_;
|
||||
my $pool=SVN::Pool->new;
|
||||
my $kind = $svn->{'svn'}->check_path(revert_split_path($branch,$path),$revision,$pool);
|
||||
$pool->clear;
|
||||
return $kind;
|
||||
}
|
||||
|
||||
sub revert_split_path($$) {
|
||||
my($branch,$path) = @_;
|
||||
|
||||
# revert split_path(), below
|
||||
my $svnpath;
|
||||
$path = "" if $path eq "/"; # this should not happen, but ...
|
||||
if($branch eq "/") {
|
||||
@ -272,6 +280,15 @@ sub get_file($$$) {
|
||||
$svnpath = "$branch_name/$branch/$path";
|
||||
}
|
||||
|
||||
$svnpath =~ s#/+$##;
|
||||
return $svnpath;
|
||||
}
|
||||
|
||||
sub get_file($$$) {
|
||||
my($rev,$branch,$path) = @_;
|
||||
|
||||
my $svnpath = revert_split_path($branch,$path);
|
||||
|
||||
# now get it
|
||||
my $name;
|
||||
if($opt_d) {
|
||||
@ -319,28 +336,61 @@ sub split_path($$) {
|
||||
} elsif($path =~ s#^/\Q$branch_name\E/([^/]+)/?##) {
|
||||
$branch = $1;
|
||||
} else {
|
||||
print STDERR "$rev: Unrecognized path: $path\n";
|
||||
my %no_error = (
|
||||
"/" => 1,
|
||||
"/$tag_name" => 1,
|
||||
"/$branch_name" => 1
|
||||
);
|
||||
print STDERR "$rev: Unrecognized path: $path\n" unless (defined $no_error{$path});
|
||||
return ()
|
||||
}
|
||||
$path = "/" if $path eq "";
|
||||
return ($branch,$path);
|
||||
}
|
||||
|
||||
sub copy_subdir($$$$$$) {
|
||||
sub branch_rev($$) {
|
||||
|
||||
my ($srcbranch,$uptorev) = @_;
|
||||
|
||||
my $bbranches = $branches{$srcbranch};
|
||||
my @revs = reverse sort { ($a eq 'LAST' ? 0 : $a) <=> ($b eq 'LAST' ? 0 : $b) } keys %$bbranches;
|
||||
my $therev;
|
||||
foreach my $arev(@revs) {
|
||||
next if ($arev eq 'LAST');
|
||||
if ($arev <= $uptorev) {
|
||||
$therev = $arev;
|
||||
last;
|
||||
}
|
||||
}
|
||||
return $therev;
|
||||
}
|
||||
|
||||
sub copy_path($$$$$$$$) {
|
||||
# Somebody copied a whole subdirectory.
|
||||
# We need to find the index entries from the old version which the
|
||||
# SVN log entry points to, and add them to the new place.
|
||||
|
||||
my($newrev,$newbranch,$path,$oldpath,$rev,$new) = @_;
|
||||
my($branch,$srcpath) = split_path($rev,$oldpath);
|
||||
my($newrev,$newbranch,$path,$oldpath,$rev,$node_kind,$new,$parents) = @_;
|
||||
|
||||
my $gitrev = $branches{$branch}{$rev};
|
||||
my($srcbranch,$srcpath) = split_path($rev,$oldpath);
|
||||
unless(defined $srcbranch) {
|
||||
print "Path not found when copying from $oldpath @ $rev\n";
|
||||
return;
|
||||
}
|
||||
my $therev = branch_rev($srcbranch, $rev);
|
||||
my $gitrev = $branches{$srcbranch}{$therev};
|
||||
unless($gitrev) {
|
||||
print STDERR "$newrev:$newbranch: could not find $oldpath \@ $rev\n";
|
||||
return;
|
||||
}
|
||||
print "$newrev:$newbranch:$path: copying from $branch:$srcpath @ $rev\n" if $opt_v;
|
||||
$srcpath =~ s#/*$#/#;
|
||||
if ($srcbranch ne $newbranch) {
|
||||
push(@$parents, $branches{$srcbranch}{'LAST'});
|
||||
}
|
||||
print "$newrev:$newbranch:$path: copying from $srcbranch:$srcpath @ $rev\n" if $opt_v;
|
||||
if ($node_kind eq $SVN::Node::dir) {
|
||||
$srcpath =~ s#/*$#/#;
|
||||
}
|
||||
|
||||
open my $f,"-|","git-ls-tree","-r","-z",$gitrev,$srcpath;
|
||||
local $/ = "\0";
|
||||
while(<$f>) {
|
||||
@ -348,9 +398,12 @@ sub copy_subdir($$$$$$) {
|
||||
my($m,$p) = split(/\t/,$_,2);
|
||||
my($mode,$type,$sha1) = split(/ /,$m);
|
||||
next if $type ne "blob";
|
||||
$p = substr($p,length($srcpath)-1);
|
||||
print "... found $path$p ...\n" if $opt_v;
|
||||
push(@$new,[$mode,$sha1,$path.$p]);
|
||||
if ($node_kind eq $SVN::Node::dir) {
|
||||
$p = $path . substr($p,length($srcpath)-1);
|
||||
} else {
|
||||
$p = $path;
|
||||
}
|
||||
push(@$new,[$mode,$sha1,$p]);
|
||||
}
|
||||
close($f) or
|
||||
print STDERR "$newrev:$newbranch: could not list files in $oldpath \@ $rev\n";
|
||||
@ -359,7 +412,7 @@ sub copy_subdir($$$$$$) {
|
||||
sub commit {
|
||||
my($branch, $changed_paths, $revision, $author, $date, $message) = @_;
|
||||
my($author_name,$author_email,$dest);
|
||||
my(@old,@new);
|
||||
my(@old,@new,@parents);
|
||||
|
||||
if (not defined $author) {
|
||||
$author_name = $author_email = "unknown";
|
||||
@ -446,6 +499,8 @@ sub commit {
|
||||
$last_rev = $rev;
|
||||
}
|
||||
|
||||
push (@parents, $rev) if defined $rev;
|
||||
|
||||
my $cid;
|
||||
if($tag and not %$changed_paths) {
|
||||
$cid = $rev;
|
||||
@ -454,39 +509,31 @@ sub commit {
|
||||
foreach my $path(@paths) {
|
||||
my $action = $changed_paths->{$path};
|
||||
|
||||
if ($action->[0] eq "A") {
|
||||
my $f = get_file($revision,$branch,$path);
|
||||
if($f) {
|
||||
push(@new,$f) if $f;
|
||||
} elsif($action->[1]) {
|
||||
copy_subdir($revision,$branch,$path,$action->[1],$action->[2],\@new);
|
||||
} else {
|
||||
my $opath = $action->[3];
|
||||
print STDERR "$revision: $branch: could not fetch '$opath'\n";
|
||||
if ($action->[0] eq "R") {
|
||||
# refer to a file/tree in an earlier commit
|
||||
push(@old,$path); # remove any old stuff
|
||||
}
|
||||
if(($action->[0] eq "A") || ($action->[0] eq "R")) {
|
||||
my $node_kind = node_kind($branch,$path,$revision);
|
||||
if($action->[1]) {
|
||||
copy_path($revision,$branch,$path,$action->[1],$action->[2],$node_kind,\@new,\@parents);
|
||||
} elsif ($node_kind eq $SVN::Node::file) {
|
||||
my $f = get_file($revision,$branch,$path);
|
||||
if ($f) {
|
||||
push(@new,$f) if $f;
|
||||
} else {
|
||||
my $opath = $action->[3];
|
||||
print STDERR "$revision: $branch: could not fetch '$opath'\n";
|
||||
}
|
||||
}
|
||||
} elsif ($action->[0] eq "D") {
|
||||
push(@old,$path);
|
||||
} elsif ($action->[0] eq "M") {
|
||||
my $f = get_file($revision,$branch,$path);
|
||||
push(@new,$f) if $f;
|
||||
} elsif ($action->[0] eq "R") {
|
||||
# refer to a file/tree in an earlier commit
|
||||
push(@old,$path); # remove any old stuff
|
||||
|
||||
# ... and add any new stuff
|
||||
my($b,$srcpath) = split_path($revision,$action->[1]);
|
||||
$srcpath =~ s#/*$#/#;
|
||||
open my $F,"-|","git-ls-tree","-r","-z", $branches{$b}{$action->[2]}, $srcpath;
|
||||
local $/ = "\0";
|
||||
while(<$F>) {
|
||||
chomp;
|
||||
my($m,$p) = split(/\t/,$_,2);
|
||||
my($mode,$type,$sha1) = split(/ /,$m);
|
||||
next if $type ne "blob";
|
||||
$p = substr($p,length($srcpath)-1);
|
||||
push(@new,[$mode,$sha1,$path.$p]);
|
||||
my $node_kind = node_kind($branch,$path,$revision);
|
||||
if ($node_kind eq $SVN::Node::file) {
|
||||
my $f = get_file($revision,$branch,$path);
|
||||
push(@new,$f) if $f;
|
||||
}
|
||||
close($F);
|
||||
} else {
|
||||
die "$revision: unknown action '".$action->[0]."' for $path\n";
|
||||
}
|
||||
@ -554,7 +601,6 @@ sub commit {
|
||||
$pw->close();
|
||||
|
||||
my @par = ();
|
||||
@par = ("-p",$rev) if defined $rev;
|
||||
|
||||
# loose detection of merges
|
||||
# based on the commit msg
|
||||
@ -564,11 +610,17 @@ sub commit {
|
||||
if ($mparent eq 'HEAD') { $mparent = $opt_o };
|
||||
if ( -e "$git_dir/refs/heads/$mparent") {
|
||||
$mparent = get_headref($mparent, $git_dir);
|
||||
push @par, '-p', $mparent;
|
||||
push (@parents, $mparent);
|
||||
print OUT "Merge parent branch: $mparent\n" if $opt_v;
|
||||
}
|
||||
}
|
||||
}
|
||||
my %seen_parents = ();
|
||||
my @unique_parents = grep { ! $seen_parents{$_} ++ } @parents;
|
||||
foreach my $bparent (@unique_parents) {
|
||||
push @par, '-p', $bparent;
|
||||
print OUT "Merge parent branch: $bparent\n" if $opt_v;
|
||||
}
|
||||
|
||||
exec("env",
|
||||
"GIT_AUTHOR_NAME=$author_name",
|
||||
@ -600,6 +652,10 @@ sub commit {
|
||||
die "Error running git-commit-tree: $?\n" if $?;
|
||||
}
|
||||
|
||||
if (not defined $cid) {
|
||||
$cid = $branches{"/"}{"LAST"};
|
||||
}
|
||||
|
||||
if(not defined $dest) {
|
||||
print "... no known parent\n" if $opt_v;
|
||||
} elsif(not $tag) {
|
||||
@ -616,6 +672,7 @@ sub commit {
|
||||
# the tag was 'complex', i.e. did not refer to a "real" revision
|
||||
|
||||
$dest =~ tr/_/\./ if $opt_u;
|
||||
$branch = $dest;
|
||||
|
||||
my $pid = open2($in, $out, 'git-mktag');
|
||||
print $out ("object $cid\n".
|
||||
@ -674,13 +731,16 @@ sub commit_all {
|
||||
}
|
||||
|
||||
while(++$current_rev <= $svn->{'maxrev'}) {
|
||||
$svn->{'svn'}->get_log("/",$current_rev,$current_rev,$current_rev,1,1,\&_commit_all,"");
|
||||
commit_all();
|
||||
if($opt_l and not --$opt_l) {
|
||||
print STDERR "Stopping, because there is a memory leak (in the SVN library).\n";
|
||||
print STDERR "Please repeat this command; it will continue safely\n";
|
||||
last;
|
||||
if (defined $opt_l) {
|
||||
$opt_l--;
|
||||
if ($opt_l < 0) {
|
||||
last;
|
||||
}
|
||||
}
|
||||
my $pool=SVN::Pool->new;
|
||||
$svn->{'svn'}->get_log("/",$current_rev,$current_rev,1,1,1,\&_commit_all,$pool);
|
||||
$pool->clear;
|
||||
commit_all();
|
||||
}
|
||||
|
||||
|
||||
|
12
git-tag.sh
12
git-tag.sh
@ -4,7 +4,7 @@
|
||||
. git-sh-setup || die "Not a git archive"
|
||||
|
||||
usage () {
|
||||
echo >&2 "Usage: git-tag [-a | -s | -u <key-id>] [-f] [-m <msg>] <tagname> [<head>]"
|
||||
echo >&2 "Usage: git-tag [-a | -s | -u <key-id>] [-f | -d] [-m <msg>] <tagname> [<head>]"
|
||||
exit 1
|
||||
}
|
||||
|
||||
@ -37,6 +37,13 @@ do
|
||||
shift
|
||||
username="$1"
|
||||
;;
|
||||
-d)
|
||||
shift
|
||||
tag_name="$1"
|
||||
rm "$GIT_DIR/refs/tags/$tag_name" && \
|
||||
echo "Deleted tag $tag_name."
|
||||
exit $?
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
;;
|
||||
@ -92,5 +99,6 @@ if [ "$annotate" ]; then
|
||||
object=$(git-mktag < "$GIT_DIR"/TAG_TMP)
|
||||
fi
|
||||
|
||||
mkdir -p "$GIT_DIR/refs/tags"
|
||||
leading=`expr "refs/tags/$name" : '\(.*\)/'` &&
|
||||
mkdir -p "$GIT_DIR/$leading" &&
|
||||
echo $object > "$GIT_DIR/refs/tags/$name"
|
||||
|
@ -1,3 +1,7 @@
|
||||
#
|
||||
# Copyright (C) 2005 Fredrik Kuivinen
|
||||
#
|
||||
|
||||
import sys, re, os, traceback
|
||||
from sets import Set
|
||||
|
||||
|
186
http-fetch.c
186
http-fetch.c
@ -25,7 +25,7 @@
|
||||
#define PREV_BUF_SIZE 4096
|
||||
#define RANGE_HEADER_SIZE 30
|
||||
|
||||
static int got_alternates = 0;
|
||||
static int got_alternates = -1;
|
||||
static int active_requests = 0;
|
||||
static int data_received;
|
||||
|
||||
@ -87,9 +87,19 @@ struct active_request_slot
|
||||
int done;
|
||||
CURLcode curl_result;
|
||||
long http_code;
|
||||
void *callback_data;
|
||||
void (*callback_func)(void *data);
|
||||
struct active_request_slot *next;
|
||||
};
|
||||
|
||||
struct alt_request {
|
||||
char *base;
|
||||
char *url;
|
||||
struct buffer *buffer;
|
||||
struct active_request_slot *slot;
|
||||
int http_specific;
|
||||
};
|
||||
|
||||
static struct transfer_request *request_queue_head = NULL;
|
||||
static struct active_request_slot *active_queue_head = NULL;
|
||||
|
||||
@ -237,7 +247,7 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
|
||||
static void process_curl_messages(void);
|
||||
static void process_request_queue(void);
|
||||
#endif
|
||||
static int fetch_alternates(char *base);
|
||||
static void fetch_alternates(char *base);
|
||||
|
||||
static CURL* get_curl_handle(void)
|
||||
{
|
||||
@ -269,6 +279,8 @@ static CURL* get_curl_handle(void)
|
||||
curl_low_speed_time);
|
||||
}
|
||||
|
||||
curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -322,6 +334,8 @@ static struct active_request_slot *get_active_slot(void)
|
||||
slot->in_use = 1;
|
||||
slot->done = 0;
|
||||
slot->local = NULL;
|
||||
slot->callback_data = NULL;
|
||||
slot->callback_func = NULL;
|
||||
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_range_header);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
|
||||
@ -569,7 +583,7 @@ static void release_request(struct transfer_request *request)
|
||||
}
|
||||
|
||||
#ifdef USE_CURL_MULTI
|
||||
void process_curl_messages(void)
|
||||
static void process_curl_messages(void)
|
||||
{
|
||||
int num_messages;
|
||||
struct active_request_slot *slot;
|
||||
@ -599,6 +613,12 @@ void process_curl_messages(void)
|
||||
} else {
|
||||
fprintf(stderr, "Received DONE message for unknown request!\n");
|
||||
}
|
||||
|
||||
/* Process slot callback if appropriate */
|
||||
if (slot->callback_func != NULL) {
|
||||
slot->callback_func(slot->callback_data);
|
||||
}
|
||||
|
||||
if (request != NULL) {
|
||||
request->curl_result = curl_result;
|
||||
request->http_code = slot->http_code;
|
||||
@ -612,6 +632,8 @@ void process_curl_messages(void)
|
||||
request->repo =
|
||||
request->repo->next;
|
||||
start_request(request);
|
||||
} else {
|
||||
finish_request(request);
|
||||
}
|
||||
} else {
|
||||
finish_request(request);
|
||||
@ -625,7 +647,7 @@ void process_curl_messages(void)
|
||||
}
|
||||
}
|
||||
|
||||
void process_request_queue(void)
|
||||
static void process_request_queue(void)
|
||||
{
|
||||
struct transfer_request *request = request_queue_head;
|
||||
struct active_request_slot *slot = active_queue_head;
|
||||
@ -764,72 +786,51 @@ static int setup_index(struct alt_base *repo, unsigned char *sha1)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fetch_alternates(char *base)
|
||||
static void process_alternates(void *callback_data)
|
||||
{
|
||||
int ret = 0;
|
||||
struct buffer buffer;
|
||||
char *url;
|
||||
struct alt_request *alt_req = (struct alt_request *)callback_data;
|
||||
struct active_request_slot *slot = alt_req->slot;
|
||||
struct alt_base *tail = alt;
|
||||
char *base = alt_req->base;
|
||||
static const char null_byte = '\0';
|
||||
char *data;
|
||||
int i = 0;
|
||||
int http_specific = 1;
|
||||
struct alt_base *tail = alt;
|
||||
static const char null_byte = '\0';
|
||||
|
||||
struct active_request_slot *slot;
|
||||
if (alt_req->http_specific) {
|
||||
if (slot->curl_result != CURLE_OK ||
|
||||
!alt_req->buffer->posn) {
|
||||
|
||||
if (got_alternates)
|
||||
return 0;
|
||||
|
||||
data = xmalloc(4096);
|
||||
buffer.size = 4096;
|
||||
buffer.posn = 0;
|
||||
buffer.buffer = data;
|
||||
|
||||
if (get_verbosely)
|
||||
fprintf(stderr, "Getting alternates list\n");
|
||||
|
||||
url = xmalloc(strlen(base) + 31);
|
||||
sprintf(url, "%s/objects/info/http-alternates", base);
|
||||
|
||||
slot = get_active_slot();
|
||||
curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
|
||||
fwrite_buffer_dynamic);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_URL, url);
|
||||
if (start_active_slot(slot)) {
|
||||
run_active_slot(slot);
|
||||
if (slot->curl_result != CURLE_OK || !buffer.posn) {
|
||||
http_specific = 0;
|
||||
|
||||
sprintf(url, "%s/objects/info/alternates", base);
|
||||
|
||||
slot = get_active_slot();
|
||||
curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
|
||||
fwrite_buffer_dynamic);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_URL, url);
|
||||
/* Try reusing the slot to get non-http alternates */
|
||||
alt_req->http_specific = 0;
|
||||
sprintf(alt_req->url, "%s/objects/info/alternates",
|
||||
base);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_URL,
|
||||
alt_req->url);
|
||||
active_requests++;
|
||||
slot->in_use = 1;
|
||||
slot->done = 0;
|
||||
if (start_active_slot(slot)) {
|
||||
run_active_slot(slot);
|
||||
if (slot->curl_result != CURLE_OK) {
|
||||
free(buffer.buffer);
|
||||
if (slot->http_code == 404)
|
||||
got_alternates = 1;
|
||||
return 0;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
got_alternates = -1;
|
||||
slot->done = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
free(buffer.buffer);
|
||||
return 0;
|
||||
} else if (slot->curl_result != CURLE_OK) {
|
||||
if (slot->http_code != 404) {
|
||||
got_alternates = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fwrite_buffer_dynamic(&null_byte, 1, 1, &buffer);
|
||||
buffer.posn--;
|
||||
data = buffer.buffer;
|
||||
fwrite_buffer_dynamic(&null_byte, 1, 1, alt_req->buffer);
|
||||
alt_req->buffer->posn--;
|
||||
data = alt_req->buffer->buffer;
|
||||
|
||||
while (i < buffer.posn) {
|
||||
while (i < alt_req->buffer->posn) {
|
||||
int posn = i;
|
||||
while (posn < buffer.posn && data[posn] != '\n')
|
||||
while (posn < alt_req->buffer->posn && data[posn] != '\n')
|
||||
posn++;
|
||||
if (data[posn] == '\n') {
|
||||
int okay = 0;
|
||||
@ -853,7 +854,7 @@ static int fetch_alternates(char *base)
|
||||
// If the server got removed, give up.
|
||||
okay = strchr(base, ':') - base + 3 <
|
||||
serverlen;
|
||||
} else if (http_specific) {
|
||||
} else if (alt_req->http_specific) {
|
||||
char *colon = strchr(data + i, ':');
|
||||
char *slash = strchr(data + i, '/');
|
||||
if (colon && slash && colon < data + posn &&
|
||||
@ -879,15 +880,74 @@ static int fetch_alternates(char *base)
|
||||
while (tail->next != NULL)
|
||||
tail = tail->next;
|
||||
tail->next = newalt;
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
i = posn + 1;
|
||||
}
|
||||
|
||||
got_alternates = 1;
|
||||
free(buffer.buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fetch_alternates(char *base)
|
||||
{
|
||||
struct buffer buffer;
|
||||
char *url;
|
||||
char *data;
|
||||
struct active_request_slot *slot;
|
||||
static struct alt_request alt_req;
|
||||
int num_transfers;
|
||||
|
||||
/* If another request has already started fetching alternates,
|
||||
wait for them to arrive and return to processing this request's
|
||||
curl message */
|
||||
while (got_alternates == 0) {
|
||||
curl_multi_perform(curlm, &num_transfers);
|
||||
process_curl_messages();
|
||||
process_request_queue();
|
||||
}
|
||||
|
||||
/* Nothing to do if they've already been fetched */
|
||||
if (got_alternates == 1)
|
||||
return;
|
||||
|
||||
/* Start the fetch */
|
||||
got_alternates = 0;
|
||||
|
||||
data = xmalloc(4096);
|
||||
buffer.size = 4096;
|
||||
buffer.posn = 0;
|
||||
buffer.buffer = data;
|
||||
|
||||
if (get_verbosely)
|
||||
fprintf(stderr, "Getting alternates list for %s\n", base);
|
||||
|
||||
url = xmalloc(strlen(base) + 31);
|
||||
sprintf(url, "%s/objects/info/http-alternates", base);
|
||||
|
||||
/* Use a callback to process the result, since another request
|
||||
may fail and need to have alternates loaded before continuing */
|
||||
slot = get_active_slot();
|
||||
slot->callback_func = process_alternates;
|
||||
slot->callback_data = &alt_req;
|
||||
|
||||
curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
|
||||
fwrite_buffer_dynamic);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_URL, url);
|
||||
|
||||
alt_req.base = base;
|
||||
alt_req.url = url;
|
||||
alt_req.buffer = &buffer;
|
||||
alt_req.http_specific = 1;
|
||||
alt_req.slot = slot;
|
||||
|
||||
if (start_active_slot(slot))
|
||||
run_active_slot(slot);
|
||||
else
|
||||
got_alternates = -1;
|
||||
|
||||
free(data);
|
||||
free(url);
|
||||
}
|
||||
|
||||
static int fetch_indices(struct alt_base *repo)
|
||||
@ -909,7 +969,7 @@ static int fetch_indices(struct alt_base *repo)
|
||||
buffer.buffer = data;
|
||||
|
||||
if (get_verbosely)
|
||||
fprintf(stderr, "Getting pack list\n");
|
||||
fprintf(stderr, "Getting pack list for %s\n", repo->base);
|
||||
|
||||
url = xmalloc(strlen(repo->base) + 21);
|
||||
sprintf(url, "%s/objects/info/packs", repo->base);
|
||||
|
50
http-push.c
50
http-push.c
@ -28,6 +28,15 @@ static const char http_push_usage[] =
|
||||
#define NO_CURL_EASY_DUPHANDLE
|
||||
#endif
|
||||
|
||||
#ifndef XML_STATUS_OK
|
||||
enum XML_Status {
|
||||
XML_STATUS_OK = 1,
|
||||
XML_STATUS_ERROR = 0
|
||||
};
|
||||
#define XML_STATUS_OK 1
|
||||
#define XML_STATUS_ERROR 0
|
||||
#endif
|
||||
|
||||
#define RANGE_HEADER_SIZE 30
|
||||
|
||||
/* DAV method names and request body templates */
|
||||
@ -47,6 +56,7 @@ static int active_requests = 0;
|
||||
static int data_received;
|
||||
static int pushing = 0;
|
||||
static int aborted = 0;
|
||||
static char remote_dir_exists[256];
|
||||
|
||||
#ifdef USE_CURL_MULTI
|
||||
static int max_requests = -1;
|
||||
@ -585,7 +595,7 @@ static void start_move(struct transfer_request *request)
|
||||
}
|
||||
}
|
||||
|
||||
int refresh_lock(struct active_lock *lock)
|
||||
static int refresh_lock(struct active_lock *lock)
|
||||
{
|
||||
struct active_request_slot *slot;
|
||||
char *if_header;
|
||||
@ -650,6 +660,7 @@ static void finish_request(struct transfer_request *request)
|
||||
if (request->http_code == 404) {
|
||||
request->state = NEED_PUSH;
|
||||
} else if (request->curl_result == CURLE_OK) {
|
||||
remote_dir_exists[request->sha1[0]] = 1;
|
||||
request->state = COMPLETE;
|
||||
} else {
|
||||
fprintf(stderr, "HEAD %s failed, aborting (%d/%ld)\n",
|
||||
@ -661,6 +672,7 @@ static void finish_request(struct transfer_request *request)
|
||||
} else if (request->state == RUN_MKCOL) {
|
||||
if (request->curl_result == CURLE_OK ||
|
||||
request->http_code == 405) {
|
||||
remote_dir_exists[request->sha1[0]] = 1;
|
||||
start_put(request);
|
||||
} else {
|
||||
fprintf(stderr, "MKCOL %s failed, aborting (%d/%ld)\n",
|
||||
@ -714,7 +726,7 @@ static void release_request(struct transfer_request *request)
|
||||
}
|
||||
|
||||
#ifdef USE_CURL_MULTI
|
||||
void process_curl_messages(void)
|
||||
static void process_curl_messages(void)
|
||||
{
|
||||
int num_messages;
|
||||
struct active_request_slot *slot;
|
||||
@ -728,11 +740,12 @@ void process_curl_messages(void)
|
||||
slot->curl != curl_message->easy_handle)
|
||||
slot = slot->next;
|
||||
if (slot != NULL) {
|
||||
int curl_result = curl_message->data.result;
|
||||
curl_multi_remove_handle(curlm, slot->curl);
|
||||
active_requests--;
|
||||
slot->done = 1;
|
||||
slot->in_use = 0;
|
||||
slot->curl_result = curl_message->data.result;
|
||||
slot->curl_result = curl_result;
|
||||
curl_easy_getinfo(slot->curl,
|
||||
CURLINFO_HTTP_CODE,
|
||||
&slot->http_code);
|
||||
@ -753,7 +766,7 @@ void process_curl_messages(void)
|
||||
}
|
||||
}
|
||||
|
||||
void process_request_queue(void)
|
||||
static void process_request_queue(void)
|
||||
{
|
||||
struct transfer_request *request = request_queue_head;
|
||||
struct active_request_slot *slot = active_queue_head;
|
||||
@ -767,7 +780,10 @@ void process_request_queue(void)
|
||||
start_check(request);
|
||||
curl_multi_perform(curlm, &num_transfers);
|
||||
} else if (pushing && request->state == NEED_PUSH) {
|
||||
start_mkcol(request);
|
||||
if (remote_dir_exists[request->sha1[0]])
|
||||
start_put(request);
|
||||
else
|
||||
start_mkcol(request);
|
||||
curl_multi_perform(curlm, &num_transfers);
|
||||
}
|
||||
request = request->next;
|
||||
@ -783,7 +799,7 @@ void process_request_queue(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
void process_waiting_requests(void)
|
||||
static void process_waiting_requests(void)
|
||||
{
|
||||
struct active_request_slot *slot = active_queue_head;
|
||||
|
||||
@ -796,7 +812,7 @@ void process_waiting_requests(void)
|
||||
}
|
||||
}
|
||||
|
||||
void add_request(unsigned char *sha1, struct active_lock *lock)
|
||||
static void add_request(unsigned char *sha1, struct active_lock *lock)
|
||||
{
|
||||
struct transfer_request *request = request_queue_head;
|
||||
struct packed_git *target;
|
||||
@ -923,7 +939,7 @@ static int setup_index(unsigned char *sha1)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fetch_indices()
|
||||
static int fetch_indices(void)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
char *url;
|
||||
@ -1173,7 +1189,7 @@ end_lockprop_element(void *userData, const char *name)
|
||||
}
|
||||
}
|
||||
|
||||
struct active_lock *lock_remote(char *file, long timeout)
|
||||
static struct active_lock *lock_remote(char *file, long timeout)
|
||||
{
|
||||
struct active_request_slot *slot;
|
||||
struct buffer out_buffer;
|
||||
@ -1230,7 +1246,7 @@ struct active_lock *lock_remote(char *file, long timeout)
|
||||
in_buffer.posn = 0;
|
||||
in_buffer.buffer = in_data;
|
||||
|
||||
new_lock = xmalloc(sizeof(*new_lock));
|
||||
new_lock = xcalloc(1, sizeof(*new_lock));
|
||||
new_lock->owner = NULL;
|
||||
new_lock->token = NULL;
|
||||
new_lock->timeout = -1;
|
||||
@ -1302,7 +1318,7 @@ struct active_lock *lock_remote(char *file, long timeout)
|
||||
return new_lock;
|
||||
}
|
||||
|
||||
int unlock_remote(struct active_lock *lock)
|
||||
static int unlock_remote(struct active_lock *lock)
|
||||
{
|
||||
struct active_request_slot *slot;
|
||||
char *lock_token_header;
|
||||
@ -1343,7 +1359,7 @@ int unlock_remote(struct active_lock *lock)
|
||||
return rc;
|
||||
}
|
||||
|
||||
int check_locking()
|
||||
static int check_locking(void)
|
||||
{
|
||||
struct active_request_slot *slot;
|
||||
struct buffer in_buffer;
|
||||
@ -1409,7 +1425,7 @@ int check_locking()
|
||||
return 1;
|
||||
}
|
||||
|
||||
int is_ancestor(unsigned char *sha1, struct commit *commit)
|
||||
static int is_ancestor(unsigned char *sha1, struct commit *commit)
|
||||
{
|
||||
struct commit_list *parents;
|
||||
|
||||
@ -1430,8 +1446,8 @@ int is_ancestor(unsigned char *sha1, struct commit *commit)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void get_delta(unsigned char *sha1, struct object *obj,
|
||||
struct active_lock *lock)
|
||||
static void get_delta(unsigned char *sha1, struct object *obj,
|
||||
struct active_lock *lock)
|
||||
{
|
||||
struct commit *commit;
|
||||
struct commit_list *parents;
|
||||
@ -1487,7 +1503,7 @@ void get_delta(unsigned char *sha1, struct object *obj,
|
||||
}
|
||||
}
|
||||
|
||||
int update_remote(unsigned char *sha1, struct active_lock *lock)
|
||||
static int update_remote(unsigned char *sha1, struct active_lock *lock)
|
||||
{
|
||||
struct active_request_slot *slot;
|
||||
char *out_data;
|
||||
@ -1599,6 +1615,8 @@ int main(int argc, char **argv)
|
||||
break;
|
||||
}
|
||||
|
||||
memset(remote_dir_exists, 0, 256);
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
#ifdef USE_CURL_MULTI
|
||||
|
@ -570,6 +570,7 @@ int main(int argc, const char **argv)
|
||||
prefix = setup_git_directory();
|
||||
if (prefix)
|
||||
prefix_offset = strlen(prefix);
|
||||
git_config(git_default_config);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
|
87
merge-base.c
87
merge-base.c
@ -80,14 +80,95 @@ static struct commit *interesting(struct commit_list *list)
|
||||
* Now, list does not have any interesting commit. So we find the newest
|
||||
* commit from the result list that is not marked uninteresting. Which is
|
||||
* commit B.
|
||||
*
|
||||
*
|
||||
* Another pathological example how this thing can fail to mark an ancestor
|
||||
* of a merge base as UNINTERESTING without the postprocessing phase.
|
||||
*
|
||||
* 2
|
||||
* H
|
||||
* 1 / \
|
||||
* G A \
|
||||
* |\ / \
|
||||
* | B \
|
||||
* | \ \
|
||||
* \ C F
|
||||
* \ \ /
|
||||
* \ D /
|
||||
* \ | /
|
||||
* \| /
|
||||
* E
|
||||
*
|
||||
* list A B C D E F G H
|
||||
* G1 H2 - - - - - - 1 2
|
||||
* H2 E1 B1 - 1 - - 1 - 1 2
|
||||
* F2 E1 B1 A2 2 1 - - 1 2 1 2
|
||||
* E3 B1 A2 2 1 - - 3 2 1 2
|
||||
* B1 A2 2 1 - - 3 2 1 2
|
||||
* C1 A2 2 1 1 - 3 2 1 2
|
||||
* D1 A2 2 1 1 1 3 2 1 2
|
||||
* A2 2 1 1 1 3 2 1 2
|
||||
* B3 2 3 1 1 3 2 1 2
|
||||
* C7 2 3 7 1 3 2 1 2
|
||||
*
|
||||
* At this point, unfortunately, everybody in the list is
|
||||
* uninteresting, so we fail to complete the following two
|
||||
* steps to fully marking uninteresting commits.
|
||||
*
|
||||
* D7 2 3 7 7 3 2 1 2
|
||||
* E7 2 3 7 7 7 2 1 2
|
||||
*
|
||||
* and we end up showing E as an interesting merge base.
|
||||
*/
|
||||
|
||||
static int show_all = 0;
|
||||
|
||||
static void mark_reachable_commits(struct commit_list *result,
|
||||
struct commit_list *list)
|
||||
{
|
||||
struct commit_list *tmp;
|
||||
|
||||
/*
|
||||
* Postprocess to fully contaminate the well.
|
||||
*/
|
||||
for (tmp = result; tmp; tmp = tmp->next) {
|
||||
struct commit *c = tmp->item;
|
||||
/* Reinject uninteresting ones to list,
|
||||
* so we can scan their parents.
|
||||
*/
|
||||
if (c->object.flags & UNINTERESTING)
|
||||
commit_list_insert(c, &list);
|
||||
}
|
||||
while (list) {
|
||||
struct commit *c = list->item;
|
||||
struct commit_list *parents;
|
||||
|
||||
tmp = list;
|
||||
list = list->next;
|
||||
free(tmp);
|
||||
|
||||
/* Anything taken out of the list is uninteresting, so
|
||||
* mark all its parents uninteresting. We do not
|
||||
* parse new ones (we already parsed all the relevant
|
||||
* ones).
|
||||
*/
|
||||
parents = c->parents;
|
||||
while (parents) {
|
||||
struct commit *p = parents->item;
|
||||
parents = parents->next;
|
||||
if (!(p->object.flags & UNINTERESTING)) {
|
||||
p->object.flags |= UNINTERESTING;
|
||||
commit_list_insert(p, &list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int merge_base(struct commit *rev1, struct commit *rev2)
|
||||
{
|
||||
struct commit_list *list = NULL;
|
||||
struct commit_list *result = NULL;
|
||||
struct commit_list *tmp = NULL;
|
||||
|
||||
if (rev1 == rev2) {
|
||||
printf("%s\n", sha1_to_hex(rev1->object.sha1));
|
||||
@ -104,9 +185,10 @@ static int merge_base(struct commit *rev1, struct commit *rev2)
|
||||
|
||||
while (interesting(list)) {
|
||||
struct commit *commit = list->item;
|
||||
struct commit_list *tmp = list, *parents;
|
||||
struct commit_list *parents;
|
||||
int flags = commit->object.flags & 7;
|
||||
|
||||
tmp = list;
|
||||
list = list->next;
|
||||
free(tmp);
|
||||
if (flags == 3) {
|
||||
@ -130,6 +212,9 @@ static int merge_base(struct commit *rev1, struct commit *rev2)
|
||||
if (!result)
|
||||
return 1;
|
||||
|
||||
if (result->next && list)
|
||||
mark_reachable_commits(result, list);
|
||||
|
||||
while (result) {
|
||||
struct commit *commit = result->item;
|
||||
result = result->next;
|
||||
|
621
pack-redundant.c
Normal file
621
pack-redundant.c
Normal file
@ -0,0 +1,621 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2005, Lukas Sandstrom <lukass@etek.chalmers.se>
|
||||
*
|
||||
* This file is licensed under the GPL v2.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cache.h"
|
||||
|
||||
static const char pack_redundant_usage[] =
|
||||
"git-pack-redundant [ --verbose ] [ --alt-odb ] < --all | <.pack filename> ...>";
|
||||
|
||||
int load_all_packs = 0, verbose = 0, alt_odb = 0;
|
||||
|
||||
struct llist_item {
|
||||
struct llist_item *next;
|
||||
char *sha1;
|
||||
};
|
||||
struct llist {
|
||||
struct llist_item *front;
|
||||
struct llist_item *back;
|
||||
size_t size;
|
||||
} *all_objects; /* all objects which must be present in local packfiles */
|
||||
|
||||
struct pack_list {
|
||||
struct pack_list *next;
|
||||
struct packed_git *pack;
|
||||
struct llist *unique_objects;
|
||||
struct llist *all_objects;
|
||||
} *local_packs = NULL, *altodb_packs = NULL;
|
||||
|
||||
struct pll {
|
||||
struct pll *next;
|
||||
struct pack_list *pl;
|
||||
};
|
||||
|
||||
inline void llist_free(struct llist *list)
|
||||
{
|
||||
while((list->back = list->front)) {
|
||||
list->front = list->front->next;
|
||||
free(list->back);
|
||||
}
|
||||
free(list);
|
||||
}
|
||||
|
||||
inline void llist_init(struct llist **list)
|
||||
{
|
||||
*list = xmalloc(sizeof(struct llist));
|
||||
(*list)->front = (*list)->back = NULL;
|
||||
(*list)->size = 0;
|
||||
}
|
||||
|
||||
struct llist * llist_copy(struct llist *list)
|
||||
{
|
||||
struct llist *ret;
|
||||
struct llist_item *new, *old, *prev;
|
||||
|
||||
llist_init(&ret);
|
||||
|
||||
if ((ret->size = list->size) == 0)
|
||||
return ret;
|
||||
|
||||
new = ret->front = xmalloc(sizeof(struct llist_item));
|
||||
new->sha1 = list->front->sha1;
|
||||
|
||||
old = list->front->next;
|
||||
while (old) {
|
||||
prev = new;
|
||||
new = xmalloc(sizeof(struct llist_item));
|
||||
prev->next = new;
|
||||
new->sha1 = old->sha1;
|
||||
old = old->next;
|
||||
}
|
||||
new->next = NULL;
|
||||
ret->back = new;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline struct llist_item * llist_insert(struct llist *list,
|
||||
struct llist_item *after, char *sha1)
|
||||
{
|
||||
struct llist_item *new = xmalloc(sizeof(struct llist_item));
|
||||
new->sha1 = sha1;
|
||||
new->next = NULL;
|
||||
|
||||
if (after != NULL) {
|
||||
new->next = after->next;
|
||||
after->next = new;
|
||||
if (after == list->back)
|
||||
list->back = new;
|
||||
} else {/* insert in front */
|
||||
if (list->size == 0)
|
||||
list->back = new;
|
||||
else
|
||||
new->next = list->front;
|
||||
list->front = new;
|
||||
}
|
||||
list->size++;
|
||||
return new;
|
||||
}
|
||||
|
||||
inline struct llist_item * llist_insert_back(struct llist *list, char *sha1)
|
||||
{
|
||||
return llist_insert(list, list->back, sha1);
|
||||
}
|
||||
|
||||
inline struct llist_item * llist_insert_sorted_unique(struct llist *list,
|
||||
char *sha1, struct llist_item *hint)
|
||||
{
|
||||
struct llist_item *prev = NULL, *l;
|
||||
|
||||
l = (hint == NULL) ? list->front : hint;
|
||||
while (l) {
|
||||
int cmp = memcmp(l->sha1, sha1, 20);
|
||||
if (cmp > 0) { /* we insert before this entry */
|
||||
return llist_insert(list, prev, sha1);
|
||||
}
|
||||
if(!cmp) { /* already exists */
|
||||
return l;
|
||||
}
|
||||
prev = l;
|
||||
l = l->next;
|
||||
}
|
||||
/* insert at the end */
|
||||
return llist_insert_back(list, sha1);
|
||||
}
|
||||
|
||||
/* computes A\B */
|
||||
void llist_sorted_difference_inplace(struct llist *A,
|
||||
struct llist *B)
|
||||
{
|
||||
struct llist_item *prev, *a, *b, *x;
|
||||
|
||||
prev = a = A->front;
|
||||
b = B->front;
|
||||
|
||||
while (a != NULL && b != NULL) {
|
||||
int cmp = memcmp(a->sha1, b->sha1, 20);
|
||||
if (!cmp) {
|
||||
x = a;
|
||||
if (a == A->front)
|
||||
A->front = a->next;
|
||||
a = prev->next = a->next;
|
||||
|
||||
if (a == NULL) /* end of list */
|
||||
A->back = prev;
|
||||
A->size--;
|
||||
free(x);
|
||||
b = b->next;
|
||||
} else
|
||||
if (cmp > 0)
|
||||
b = b->next;
|
||||
else {
|
||||
prev = a;
|
||||
a = a->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* returns a pointer to an item in front of sha1 */
|
||||
inline struct llist_item * llist_sorted_remove(struct llist *list, char *sha1,
|
||||
struct llist_item *hint)
|
||||
{
|
||||
struct llist_item *prev, *l;
|
||||
|
||||
redo_from_start:
|
||||
l = (hint == NULL) ? list->front : hint;
|
||||
prev = NULL;
|
||||
while (l) {
|
||||
int cmp = memcmp(l->sha1, sha1, 20);
|
||||
if (cmp > 0) /* not in list, since sorted */
|
||||
return prev;
|
||||
if(!cmp) { /* found */
|
||||
if (prev == NULL) {
|
||||
if (hint != NULL && hint != list->front) {
|
||||
/* we don't know the previous element */
|
||||
hint = NULL;
|
||||
goto redo_from_start;
|
||||
}
|
||||
list->front = l->next;
|
||||
} else
|
||||
prev->next = l->next;
|
||||
if (l == list->back)
|
||||
list->back = prev;
|
||||
free(l);
|
||||
list->size--;
|
||||
return prev;
|
||||
}
|
||||
prev = l;
|
||||
l = l->next;
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
inline struct pack_list * pack_list_insert(struct pack_list **pl,
|
||||
struct pack_list *entry)
|
||||
{
|
||||
struct pack_list *p = xmalloc(sizeof(struct pack_list));
|
||||
memcpy(p, entry, sizeof(struct pack_list));
|
||||
p->next = *pl;
|
||||
*pl = p;
|
||||
return p;
|
||||
}
|
||||
|
||||
inline size_t pack_list_size(struct pack_list *pl)
|
||||
{
|
||||
size_t ret = 0;
|
||||
while(pl) {
|
||||
ret++;
|
||||
pl = pl->next;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct pack_list * pack_list_difference(struct pack_list *A,
|
||||
struct pack_list *B)
|
||||
{
|
||||
struct pack_list *ret, *pl;
|
||||
|
||||
if (A == NULL)
|
||||
return NULL;
|
||||
|
||||
pl = B;
|
||||
while (pl != NULL) {
|
||||
if (A->pack == pl->pack)
|
||||
return pack_list_difference(A->next, B);
|
||||
pl = pl->next;
|
||||
}
|
||||
ret = xmalloc(sizeof(struct pack_list));
|
||||
memcpy(ret, A, sizeof(struct pack_list));
|
||||
ret->next = pack_list_difference(A->next, B);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
|
||||
{
|
||||
int p1_off, p2_off;
|
||||
void *p1_base, *p2_base;
|
||||
struct llist_item *p1_hint = NULL, *p2_hint = NULL;
|
||||
|
||||
p1_off = p2_off = 256 * 4 + 4;
|
||||
p1_base = (void *)p1->pack->index_base;
|
||||
p2_base = (void *)p2->pack->index_base;
|
||||
|
||||
while (p1_off <= p1->pack->index_size - 3 * 20 &&
|
||||
p2_off <= p2->pack->index_size - 3 * 20)
|
||||
{
|
||||
int cmp = memcmp(p1_base + p1_off, p2_base + p2_off, 20);
|
||||
/* cmp ~ p1 - p2 */
|
||||
if (cmp == 0) {
|
||||
p1_hint = llist_sorted_remove(p1->unique_objects,
|
||||
p1_base + p1_off, p1_hint);
|
||||
p2_hint = llist_sorted_remove(p2->unique_objects,
|
||||
p1_base + p1_off, p2_hint);
|
||||
p1_off+=24;
|
||||
p2_off+=24;
|
||||
continue;
|
||||
}
|
||||
if (cmp < 0) { /* p1 has the object, p2 doesn't */
|
||||
p1_off+=24;
|
||||
} else { /* p2 has the object, p1 doesn't */
|
||||
p2_off+=24;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* all the permutations have to be free()d at the same time,
|
||||
* since they refer to each other
|
||||
*/
|
||||
struct pll * get_all_permutations(struct pack_list *list)
|
||||
{
|
||||
struct pll *subset, *pll, *new_pll = NULL; /*silence warning*/
|
||||
|
||||
if (list == NULL)
|
||||
return NULL;
|
||||
|
||||
if (list->next == NULL) {
|
||||
new_pll = xmalloc(sizeof(struct pll));
|
||||
new_pll->next = NULL;
|
||||
new_pll->pl = list;
|
||||
return new_pll;
|
||||
}
|
||||
|
||||
pll = subset = get_all_permutations(list->next);
|
||||
while (pll) {
|
||||
new_pll = xmalloc(sizeof(struct pll));
|
||||
new_pll->next = pll->next;
|
||||
pll->next = new_pll;
|
||||
|
||||
new_pll->pl = xmalloc(sizeof(struct pack_list));
|
||||
memcpy(new_pll->pl, list, sizeof(struct pack_list));
|
||||
new_pll->pl->next = pll->pl;
|
||||
|
||||
pll = new_pll->next;
|
||||
}
|
||||
/* add ourself to the end */
|
||||
new_pll->next = xmalloc(sizeof(struct pll));
|
||||
new_pll->next->pl = xmalloc(sizeof(struct pack_list));
|
||||
new_pll->next->next = NULL;
|
||||
memcpy(new_pll->next->pl, list, sizeof(struct pack_list));
|
||||
new_pll->next->pl->next = NULL;
|
||||
|
||||
return subset;
|
||||
}
|
||||
|
||||
int is_superset(struct pack_list *pl, struct llist *list)
|
||||
{
|
||||
struct llist *diff;
|
||||
|
||||
diff = llist_copy(list);
|
||||
|
||||
while (pl) {
|
||||
llist_sorted_difference_inplace(diff,
|
||||
pl->all_objects);
|
||||
if (diff->size == 0) { /* we're done */
|
||||
llist_free(diff);
|
||||
return 1;
|
||||
}
|
||||
pl = pl->next;
|
||||
}
|
||||
llist_free(diff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
|
||||
{
|
||||
size_t ret = 0;
|
||||
int p1_off, p2_off;
|
||||
void *p1_base, *p2_base;
|
||||
|
||||
p1_off = p2_off = 256 * 4 + 4;
|
||||
p1_base = (void *)p1->index_base;
|
||||
p2_base = (void *)p2->index_base;
|
||||
|
||||
while (p1_off <= p1->index_size - 3 * 20 &&
|
||||
p2_off <= p2->index_size - 3 * 20)
|
||||
{
|
||||
int cmp = memcmp(p1_base + p1_off, p2_base + p2_off, 20);
|
||||
/* cmp ~ p1 - p2 */
|
||||
if (cmp == 0) {
|
||||
ret++;
|
||||
p1_off+=24;
|
||||
p2_off+=24;
|
||||
continue;
|
||||
}
|
||||
if (cmp < 0) { /* p1 has the object, p2 doesn't */
|
||||
p1_off+=24;
|
||||
} else { /* p2 has the object, p1 doesn't */
|
||||
p2_off+=24;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* another O(n^2) function ... */
|
||||
size_t get_pack_redundancy(struct pack_list *pl)
|
||||
{
|
||||
struct pack_list *subset;
|
||||
|
||||
if (pl == NULL)
|
||||
return 0;
|
||||
|
||||
size_t ret = 0;
|
||||
while ((subset = pl->next)) {
|
||||
while(subset) {
|
||||
ret += sizeof_union(pl->pack, subset->pack);
|
||||
subset = subset->next;
|
||||
}
|
||||
pl = pl->next;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline size_t pack_set_bytecount(struct pack_list *pl)
|
||||
{
|
||||
size_t ret = 0;
|
||||
while (pl) {
|
||||
ret += pl->pack->pack_size;
|
||||
ret += pl->pack->index_size;
|
||||
pl = pl->next;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void minimize(struct pack_list **min)
|
||||
{
|
||||
struct pack_list *pl, *unique = NULL,
|
||||
*non_unique = NULL, *min_perm = NULL;
|
||||
struct pll *perm, *perm_all, *perm_ok = NULL, *new_perm;
|
||||
struct llist *missing;
|
||||
size_t min_perm_size = (size_t)-1, perm_size;
|
||||
|
||||
pl = local_packs;
|
||||
while (pl) {
|
||||
if(pl->unique_objects->size)
|
||||
pack_list_insert(&unique, pl);
|
||||
else
|
||||
pack_list_insert(&non_unique, pl);
|
||||
pl = pl->next;
|
||||
}
|
||||
/* find out which objects are missing from the set of unique packs */
|
||||
missing = llist_copy(all_objects);
|
||||
pl = unique;
|
||||
while (pl) {
|
||||
llist_sorted_difference_inplace(missing,
|
||||
pl->all_objects);
|
||||
pl = pl->next;
|
||||
}
|
||||
|
||||
/* return if there are no objects missing from the unique set */
|
||||
if (missing->size == 0) {
|
||||
*min = unique;
|
||||
return;
|
||||
}
|
||||
|
||||
/* find the permutations which contain all missing objects */
|
||||
perm_all = perm = get_all_permutations(non_unique);
|
||||
while (perm) {
|
||||
if (is_superset(perm->pl, missing)) {
|
||||
new_perm = xmalloc(sizeof(struct pll));
|
||||
new_perm->pl = perm->pl;
|
||||
new_perm->next = perm_ok;
|
||||
perm_ok = new_perm;
|
||||
}
|
||||
perm = perm->next;
|
||||
}
|
||||
|
||||
if (perm_ok == NULL)
|
||||
die("Internal error: No complete sets found!\n");
|
||||
|
||||
/* find the permutation with the smallest size */
|
||||
perm = perm_ok;
|
||||
while (perm) {
|
||||
perm_size = pack_set_bytecount(perm->pl);
|
||||
if (min_perm_size > perm_size) {
|
||||
min_perm_size = perm_size;
|
||||
min_perm = perm->pl;
|
||||
}
|
||||
perm = perm->next;
|
||||
}
|
||||
*min = min_perm;
|
||||
/* add the unique packs to the list */
|
||||
pl = unique;
|
||||
while(pl) {
|
||||
pack_list_insert(min, pl);
|
||||
pl = pl->next;
|
||||
}
|
||||
}
|
||||
|
||||
void load_all_objects()
|
||||
{
|
||||
struct pack_list *pl = local_packs;
|
||||
struct llist_item *hint, *l;
|
||||
int i;
|
||||
|
||||
llist_init(&all_objects);
|
||||
|
||||
while (pl) {
|
||||
i = 0;
|
||||
hint = NULL;
|
||||
l = pl->all_objects->front;
|
||||
while (l) {
|
||||
hint = llist_insert_sorted_unique(all_objects,
|
||||
l->sha1, hint);
|
||||
l = l->next;
|
||||
}
|
||||
pl = pl->next;
|
||||
}
|
||||
/* remove objects present in remote packs */
|
||||
pl = altodb_packs;
|
||||
while (pl) {
|
||||
llist_sorted_difference_inplace(all_objects, pl->all_objects);
|
||||
pl = pl->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* this scales like O(n^2) */
|
||||
void cmp_packs()
|
||||
{
|
||||
struct pack_list *subset, *pl = local_packs;
|
||||
|
||||
while ((subset = pl)) {
|
||||
while((subset = subset->next))
|
||||
cmp_two_packs(pl, subset);
|
||||
pl = pl->next;
|
||||
}
|
||||
|
||||
pl = altodb_packs;
|
||||
while (pl) {
|
||||
subset = local_packs;
|
||||
while (subset) {
|
||||
llist_sorted_difference_inplace(subset->unique_objects,
|
||||
pl->all_objects);
|
||||
subset = subset->next;
|
||||
}
|
||||
pl = pl->next;
|
||||
}
|
||||
}
|
||||
|
||||
struct pack_list * add_pack(struct packed_git *p)
|
||||
{
|
||||
struct pack_list l;
|
||||
size_t off;
|
||||
void *base;
|
||||
|
||||
l.pack = p;
|
||||
llist_init(&l.all_objects);
|
||||
|
||||
off = 256 * 4 + 4;
|
||||
base = (void *)p->index_base;
|
||||
while (off <= p->index_size - 3 * 20) {
|
||||
llist_insert_back(l.all_objects, base + off);
|
||||
off+=24;
|
||||
}
|
||||
/* this list will be pruned in cmp_two_packs later */
|
||||
l.unique_objects = llist_copy(l.all_objects);
|
||||
if (p->pack_local)
|
||||
return pack_list_insert(&local_packs, &l);
|
||||
else
|
||||
return alt_odb ? pack_list_insert(&altodb_packs, &l) : NULL;
|
||||
}
|
||||
|
||||
struct pack_list * add_pack_file(char *filename)
|
||||
{
|
||||
struct packed_git *p = packed_git;
|
||||
|
||||
if (strlen(filename) < 40)
|
||||
die("Bad pack filename: %s\n", filename);
|
||||
|
||||
while (p) {
|
||||
if (strstr(p->pack_name, filename))
|
||||
return add_pack(p);
|
||||
p = p->next;
|
||||
}
|
||||
die("Filename %s not found in packed_git\n", filename);
|
||||
}
|
||||
|
||||
void load_all()
|
||||
{
|
||||
struct packed_git *p = packed_git;
|
||||
|
||||
while (p) {
|
||||
add_pack(p);
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
struct pack_list *min, *red, *pl;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
if(!strcmp(arg, "--")) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
if(!strcmp(arg, "--all")) {
|
||||
load_all_packs = 1;
|
||||
continue;
|
||||
}
|
||||
if(!strcmp(arg, "--verbose")) {
|
||||
verbose = 1;
|
||||
continue;
|
||||
}
|
||||
if(!strcmp(arg, "--alt-odb")) {
|
||||
alt_odb = 1;
|
||||
continue;
|
||||
}
|
||||
if(*arg == '-')
|
||||
usage(pack_redundant_usage);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
prepare_packed_git();
|
||||
|
||||
if (load_all_packs)
|
||||
load_all();
|
||||
else
|
||||
while (*(argv + i) != NULL)
|
||||
add_pack_file(*(argv + i++));
|
||||
|
||||
if (local_packs == NULL)
|
||||
die("Zero packs found!\n");
|
||||
|
||||
cmp_packs();
|
||||
|
||||
load_all_objects();
|
||||
|
||||
minimize(&min);
|
||||
if (verbose) {
|
||||
fprintf(stderr, "There are %lu packs available in alt-odbs.\n",
|
||||
(unsigned long)pack_list_size(altodb_packs));
|
||||
fprintf(stderr, "The smallest (bytewise) set of packs is:\n");
|
||||
pl = min;
|
||||
while (pl) {
|
||||
fprintf(stderr, "\t%s\n", pl->pack->pack_name);
|
||||
pl = pl->next;
|
||||
}
|
||||
fprintf(stderr, "containing %lu duplicate objects "
|
||||
"with a total size of %lukb.\n",
|
||||
(unsigned long)get_pack_redundancy(min),
|
||||
(unsigned long)pack_set_bytecount(min)/1024);
|
||||
fprintf(stderr, "A total of %lu unique objects were considered.\n",
|
||||
(unsigned long)all_objects->size);
|
||||
fprintf(stderr, "Redundant packs (with indexes):\n");
|
||||
}
|
||||
pl = red = pack_list_difference(local_packs, min);
|
||||
while (pl) {
|
||||
printf("%s\n%s\n",
|
||||
sha1_pack_index_name(pl->pack->sha1),
|
||||
pl->pack->pack_name);
|
||||
pl = pl->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -633,6 +633,8 @@ int main(int argc, char **argv)
|
||||
if (newfd < 0)
|
||||
die("unable to create new cachefile");
|
||||
|
||||
git_config(git_default_config);
|
||||
|
||||
merge = 0;
|
||||
reset = 0;
|
||||
for (i = 1; i < argc; i++) {
|
||||
|
@ -424,6 +424,7 @@ struct packed_git *add_packed_git(char *path, int path_len, int local)
|
||||
struct packed_git *p;
|
||||
unsigned long idx_size;
|
||||
void *idx_map;
|
||||
char sha1[20];
|
||||
|
||||
if (check_packed_git_idx(path, &idx_size, &idx_map))
|
||||
return NULL;
|
||||
@ -447,6 +448,8 @@ struct packed_git *add_packed_git(char *path, int path_len, int local)
|
||||
p->pack_last_used = 0;
|
||||
p->pack_use_cnt = 0;
|
||||
p->pack_local = local;
|
||||
if (!get_sha1_hex(path + path_len - 40 - 4, sha1))
|
||||
memcpy(p->sha1, sha1, 20);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
@ -181,11 +181,11 @@ static void join_revs(struct commit_list **list_p,
|
||||
|
||||
while (*list_p) {
|
||||
struct commit_list *parents;
|
||||
int still_interesting = !!interesting(*list_p);
|
||||
struct commit *commit = pop_one_commit(list_p);
|
||||
int flags = commit->object.flags & all_mask;
|
||||
int still_interesting = !!interesting(*list_p);
|
||||
|
||||
if (!still_interesting && extra < 0)
|
||||
if (!still_interesting && extra <= 0)
|
||||
break;
|
||||
|
||||
mark_seen(commit, seen_p);
|
||||
@ -199,18 +199,58 @@ static void join_revs(struct commit_list **list_p,
|
||||
parents = parents->next;
|
||||
if ((this_flag & flags) == flags)
|
||||
continue;
|
||||
parse_commit(p);
|
||||
if (!p->object.parsed)
|
||||
parse_commit(p);
|
||||
if (mark_seen(p, seen_p) && !still_interesting)
|
||||
extra--;
|
||||
p->object.flags |= flags;
|
||||
insert_by_date(p, list_p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Postprocess to complete well-poisoning.
|
||||
*
|
||||
* At this point we have all the commits we have seen in
|
||||
* seen_p list (which happens to be sorted chronologically but
|
||||
* it does not really matter). Mark anything that can be
|
||||
* reached from uninteresting commits not interesting.
|
||||
*/
|
||||
for (;;) {
|
||||
int changed = 0;
|
||||
struct commit_list *s;
|
||||
for (s = *seen_p; s; s = s->next) {
|
||||
struct commit *c = s->item;
|
||||
struct commit_list *parents;
|
||||
|
||||
if (((c->object.flags & all_revs) != all_revs) &&
|
||||
!(c->object.flags & UNINTERESTING))
|
||||
continue;
|
||||
|
||||
/* The current commit is either a merge base or
|
||||
* already uninteresting one. Mark its parents
|
||||
* as uninteresting commits _only_ if they are
|
||||
* already parsed. No reason to find new ones
|
||||
* here.
|
||||
*/
|
||||
parents = c->parents;
|
||||
while (parents) {
|
||||
struct commit *p = parents->item;
|
||||
parents = parents->next;
|
||||
if (!(p->object.flags & UNINTERESTING)) {
|
||||
p->object.flags |= UNINTERESTING;
|
||||
changed = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!changed)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void show_one_commit(struct commit *commit, int no_name)
|
||||
{
|
||||
char pretty[128], *cp;
|
||||
char pretty[256], *cp;
|
||||
struct commit_name *name = commit->object.util;
|
||||
if (commit->object.parsed)
|
||||
pretty_print_commit(CMIT_FMT_ONELINE, commit->buffer, ~0,
|
||||
@ -360,7 +400,7 @@ int main(int ac, char **av)
|
||||
unsigned int rev_mask[MAX_REVS];
|
||||
int num_rev, i, extra = 0;
|
||||
int all_heads = 0, all_tags = 0;
|
||||
int all_mask, all_revs, shown_merge_point;
|
||||
int all_mask, all_revs;
|
||||
char head_path[128];
|
||||
const char *head_path_p;
|
||||
int head_path_len;
|
||||
@ -369,6 +409,8 @@ int main(int ac, char **av)
|
||||
int independent = 0;
|
||||
int no_name = 0;
|
||||
int sha1_name = 0;
|
||||
int shown_merge_point = 0;
|
||||
int topo_order = 0;
|
||||
|
||||
setup_git_directory();
|
||||
|
||||
@ -394,6 +436,8 @@ int main(int ac, char **av)
|
||||
merge_base = 1;
|
||||
else if (!strcmp(arg, "--independent"))
|
||||
independent = 1;
|
||||
else if (!strcmp(arg, "--topo-order"))
|
||||
topo_order = 1;
|
||||
else
|
||||
usage(show_branch_usage);
|
||||
ac--; av++;
|
||||
@ -496,7 +540,8 @@ int main(int ac, char **av)
|
||||
exit(0);
|
||||
|
||||
/* Sort topologically */
|
||||
sort_in_topological_order(&seen);
|
||||
if (topo_order)
|
||||
sort_in_topological_order(&seen);
|
||||
|
||||
/* Give names to commits */
|
||||
if (!sha1_name && !no_name)
|
||||
@ -504,15 +549,12 @@ int main(int ac, char **av)
|
||||
|
||||
all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
|
||||
all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
|
||||
shown_merge_point = 0;
|
||||
|
||||
while (seen) {
|
||||
struct commit *commit = pop_one_commit(&seen);
|
||||
int this_flag = commit->object.flags;
|
||||
int is_merge_point = (this_flag & all_revs) == all_revs;
|
||||
|
||||
if (is_merge_point)
|
||||
shown_merge_point = 1;
|
||||
shown_merge_point |= ((this_flag & all_revs) == all_revs);
|
||||
|
||||
if (1 < num_rev) {
|
||||
for (i = 0; i < num_rev; i++)
|
||||
@ -521,9 +563,9 @@ int main(int ac, char **av)
|
||||
putchar(' ');
|
||||
}
|
||||
show_one_commit(commit, no_name);
|
||||
if (shown_merge_point && is_merge_point)
|
||||
if (--extra < 0)
|
||||
break;
|
||||
|
||||
if (shown_merge_point && --extra < 0)
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
11
t/Makefile
11
t/Makefile
@ -15,9 +15,14 @@ shellquote = '$(call shq,$(1))'
|
||||
|
||||
T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
|
||||
|
||||
all:
|
||||
@$(foreach t,$T,echo "*** $t ***"; $(call shellquote,$(SHELL_PATH)) $t $(GIT_TEST_OPTS) || exit; )
|
||||
@rm -fr trash
|
||||
all: $(T) clean
|
||||
|
||||
$(T):
|
||||
@echo "*** $@ ***"; $(call shellquote,$(SHELL_PATH)) $@ $(GIT_TEST_OPTS)
|
||||
|
||||
clean:
|
||||
rm -fr trash
|
||||
|
||||
.PHONY: $(T) clean
|
||||
.NOPARALLEL:
|
||||
|
||||
|
@ -122,7 +122,7 @@ cat > show-branch.expect << EOF
|
||||
++ [mybranch] Some work.
|
||||
EOF
|
||||
|
||||
git show-branch master mybranch > show-branch.output
|
||||
git show-branch --topo-order master mybranch > show-branch.output
|
||||
test_expect_success 'git show-branch' 'cmp show-branch.expect show-branch.output'
|
||||
|
||||
git checkout mybranch
|
||||
@ -145,7 +145,7 @@ cat > show-branch2.expect << EOF
|
||||
++ [master] Merged "mybranch" changes.
|
||||
EOF
|
||||
|
||||
git show-branch master mybranch > show-branch2.output
|
||||
git show-branch --topo-order master mybranch > show-branch2.output
|
||||
test_expect_success 'git show-branch' 'cmp show-branch2.expect show-branch2.output'
|
||||
|
||||
# TODO: test git fetch
|
||||
|
@ -24,4 +24,12 @@ test_expect_failure \
|
||||
'git branch --help should not have created a bogus branch' \
|
||||
'test -f .git/refs/heads/--help'
|
||||
|
||||
test_expect_success \
|
||||
'git branch abc should create a branch' \
|
||||
'git-branch abc && test -f .git/refs/heads/abc'
|
||||
|
||||
test_expect_success \
|
||||
'git branch a/b/c should create a branch' \
|
||||
'git-branch a/b/c && test -f .git/refs/heads/a/b/c'
|
||||
|
||||
test_done
|
||||
|
@ -25,6 +25,7 @@ commit id embedding:
|
||||
'
|
||||
|
||||
. ./test-lib.sh
|
||||
TAR=${TAR:-tar}
|
||||
|
||||
test_expect_success \
|
||||
'populate workdir' \
|
||||
|
@ -25,7 +25,7 @@ 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:09" save_tag b3 unique_commit b3 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
|
||||
@ -116,15 +116,15 @@ g0
|
||||
EOF
|
||||
|
||||
test_output_expect_success 'multiple heads' 'git-rev-list --topo-order a3 b3 c3' <<EOF
|
||||
b3
|
||||
c3
|
||||
c2
|
||||
b2
|
||||
c1
|
||||
b1
|
||||
a3
|
||||
a2
|
||||
a1
|
||||
c3
|
||||
c2
|
||||
c1
|
||||
b3
|
||||
b2
|
||||
b1
|
||||
a0
|
||||
l2
|
||||
l1
|
||||
@ -133,26 +133,26 @@ root
|
||||
EOF
|
||||
|
||||
test_output_expect_success 'multiple heads, prune at a1' 'git-rev-list --topo-order a3 b3 c3 ^a1' <<EOF
|
||||
b3
|
||||
c3
|
||||
c2
|
||||
b2
|
||||
c1
|
||||
b1
|
||||
a3
|
||||
a2
|
||||
c3
|
||||
c2
|
||||
c1
|
||||
b3
|
||||
b2
|
||||
b1
|
||||
EOF
|
||||
|
||||
test_output_expect_success 'multiple heads, prune at l1' 'git-rev-list --topo-order a3 b3 c3 ^l1' <<EOF
|
||||
b3
|
||||
c3
|
||||
c2
|
||||
b2
|
||||
c1
|
||||
b1
|
||||
a3
|
||||
a2
|
||||
a1
|
||||
c3
|
||||
c2
|
||||
c1
|
||||
b3
|
||||
b2
|
||||
b1
|
||||
a0
|
||||
l2
|
||||
EOF
|
||||
|
59
t/t6010-merge-base.sh
Executable file
59
t/t6010-merge-base.sh
Executable file
@ -0,0 +1,59 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005 Junio C Hamano
|
||||
#
|
||||
|
||||
test_description='Merge base computation.
|
||||
'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
T=$(git-write-tree)
|
||||
|
||||
M=1130000000
|
||||
Z=+0000
|
||||
|
||||
export GIT_COMMITTER_EMAIL=git@comm.iter.xz
|
||||
export GIT_COMMITTER_NAME='C O Mmiter'
|
||||
export GIT_AUTHOR_NAME='A U Thor'
|
||||
export GIT_AUTHOR_EMAIL=git@au.thor.xz
|
||||
|
||||
doit() {
|
||||
OFFSET=$1; shift
|
||||
NAME=$1; shift
|
||||
PARENTS=
|
||||
for P
|
||||
do
|
||||
PARENTS="${PARENTS}-p $P "
|
||||
done
|
||||
GIT_COMMITTER_DATE="$(($M + $OFFSET)) $Z"
|
||||
GIT_AUTHOR_DATE=$GIT_COMMITTER_DATE
|
||||
export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
|
||||
commit=$(echo $NAME | git-commit-tree $T $PARENTS)
|
||||
echo $commit >.git/refs/tags/$NAME
|
||||
echo $commit
|
||||
}
|
||||
|
||||
# Setup...
|
||||
E=$(doit 5 E)
|
||||
D=$(doit 4 D $E)
|
||||
F=$(doit 6 F $E)
|
||||
C=$(doit 3 C $D)
|
||||
B=$(doit 2 B $C)
|
||||
A=$(doit 1 A $B)
|
||||
G=$(doit 7 G $B $E)
|
||||
H=$(doit 8 H $A $F)
|
||||
|
||||
test_expect_success 'compute merge-base (single)' \
|
||||
'MB=$(git-merge-base G H) &&
|
||||
expr "$(git-name-rev "$MB")" : "[0-9a-f]* B"'
|
||||
|
||||
test_expect_success 'compute merge-base (all)' \
|
||||
'MB=$(git-merge-base --all G H) &&
|
||||
expr "$(git-name-rev "$MB")" : "[0-9a-f]* B"'
|
||||
|
||||
test_expect_success 'compute merge-base with show-branch' \
|
||||
'MB=$(git-show-branch --merge-base G H) &&
|
||||
expr "$(git-name-rev "$MB")" : "[0-9a-f]* B"'
|
||||
|
||||
test_done
|
@ -13,7 +13,6 @@ shq = $(subst ','\'',$(1))
|
||||
shellquote = '$(call shq,$(1))'
|
||||
|
||||
all: boilerplates.made custom
|
||||
find blt
|
||||
|
||||
# Put templates that can be copied straight from the source
|
||||
# in a file direc--tory--file in the source. They will be
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <sys/time.h>
|
||||
|
||||
static int dry_run, quiet;
|
||||
static const char unpack_usage[] = "git-unpack-objects [-q] < pack-file";
|
||||
static const char unpack_usage[] = "git-unpack-objects [-n] [-q] < pack-file";
|
||||
|
||||
/* We always read in 4kB chunks. */
|
||||
static unsigned char buffer[4096];
|
||||
|
@ -19,7 +19,8 @@ static int re_verify(const char *path, unsigned char *oldsha1, unsigned char *cu
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *hex;
|
||||
const char *refname, *value, *oldval, *path, *lockpath;
|
||||
const char *refname, *value, *oldval, *path;
|
||||
char *lockpath;
|
||||
unsigned char sha1[20], oldsha1[20], currsha1[20];
|
||||
int fd, written;
|
||||
|
||||
@ -42,13 +43,15 @@ int main(int argc, char **argv)
|
||||
|
||||
if (oldval) {
|
||||
if (memcmp(currsha1, oldsha1, 20))
|
||||
die("Ref %s changed to %s", refname, sha1_to_hex(currsha1));
|
||||
die("Ref %s is at %s but expected %s", refname, sha1_to_hex(currsha1), sha1_to_hex(oldsha1));
|
||||
/* Nothing to do? */
|
||||
if (!memcmp(oldsha1, sha1, 20))
|
||||
exit(0);
|
||||
}
|
||||
path = strdup(path);
|
||||
lockpath = mkpath("%s.lock", path);
|
||||
if (safe_create_leading_directories(lockpath) < 0)
|
||||
die("Unable to create all of %s", lockpath);
|
||||
|
||||
fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
|
||||
if (fd < 0)
|
||||
|
Reference in New Issue
Block a user