mirror of
https://github.com/git/git.git
synced 2026-03-13 10:23:30 +01:00
Merge commit 'mingw/master' into work/merge-mingw
This commit is contained in:
@@ -6,6 +6,14 @@ Updates since v1.5.3
|
||||
|
||||
* Comes with much improved gitk.
|
||||
|
||||
* Comes with git-gui 0.9.0 with i18n.
|
||||
|
||||
* git-lost-found was deprecated in favor of git-fsck's --lost-found
|
||||
option.
|
||||
|
||||
* git-peek-remote is deprecated, as git-ls-remote was written in C and
|
||||
works for all transports.
|
||||
|
||||
* "progress display" from many commands are a lot nicer to the
|
||||
eye. Transfer commands show throughput data.
|
||||
|
||||
@@ -15,6 +23,11 @@ Updates since v1.5.3
|
||||
|
||||
* git-rebase learned --whitespace option.
|
||||
|
||||
* In git-rebase, when you decide not to replay a particular change
|
||||
after the command stopped with a conflict, you can say "git-rebase
|
||||
--skip" without first running "git reset --hard", as the command now
|
||||
runs it for you.
|
||||
|
||||
* git-remote knows --mirror mode.
|
||||
|
||||
* git-merge can call the "post-merge" hook.
|
||||
@@ -37,11 +50,20 @@ Updates since v1.5.3
|
||||
variable used to mean "do not require", but we now use the safer
|
||||
default).
|
||||
|
||||
* git-clean has been rewritten in C.
|
||||
|
||||
* git-push has been rewritten in C.
|
||||
|
||||
* git-push learned --dry-run option to show what would happen
|
||||
if a push is run.
|
||||
|
||||
* git-push does not update a tracking ref on the pushing side when the
|
||||
remote refused to update the corresponding ref.
|
||||
|
||||
* git-push learned --mirror option. This is to push the local refs
|
||||
one-to-one to the remote, and deletes refs from the remote that do
|
||||
not exist anymore in the repository on the pushing side.
|
||||
|
||||
* git-remote learned "rm" subcommand.
|
||||
|
||||
* git-rebase --interactive mode can now work on detached HEAD.
|
||||
@@ -54,9 +76,6 @@ Updates since v1.5.3
|
||||
|
||||
* Various Perforce importer updates.
|
||||
|
||||
* git-lost-found was deprecated in favor of git-fsck's --lost-found
|
||||
option.
|
||||
|
||||
* "git log" learned --early-output option to help interactive
|
||||
GUI implementations.
|
||||
|
||||
@@ -86,6 +105,17 @@ Updates since v1.5.3
|
||||
to allow checking out a path outside the current directory
|
||||
without cd'ing up.
|
||||
|
||||
* "git send-email --dry-run" shows full headers for easier
|
||||
diagnosis.
|
||||
|
||||
* "git merge-ours" is built-in.
|
||||
|
||||
* "git svn" learned "info" subcommand.
|
||||
|
||||
* "git status" from a subdirectory now shows relative paths
|
||||
which makes copy-and-pasting for git-checkout/git-add/git-rm
|
||||
easier.
|
||||
|
||||
* Output processing for '--pretty=format:<user format>' has
|
||||
been optimized.
|
||||
|
||||
@@ -119,6 +149,6 @@ this release, unless otherwise noted.
|
||||
|
||||
--
|
||||
exec >/var/tmp/1
|
||||
O=v1.5.3.6-727-g5d3d1ca
|
||||
O=v1.5.3.6-950-gda03a58
|
||||
echo O=`git describe refs/heads/master`
|
||||
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
|
||||
|
||||
@@ -151,7 +151,7 @@ git-pack-redundant plumbinginterrogators
|
||||
git-pack-refs ancillarymanipulators
|
||||
git-parse-remote synchelpers
|
||||
git-patch-id purehelpers
|
||||
git-peek-remote purehelpers
|
||||
git-peek-remote purehelpers deprecated
|
||||
git-prune ancillarymanipulators
|
||||
git-prune-packed plumbingmanipulators
|
||||
git-pull mainporcelain
|
||||
|
||||
@@ -45,17 +45,22 @@ to happen.
|
||||
|
||||
With a `-d` or `-D` option, `<branchname>` will be deleted. You may
|
||||
specify more than one branch for deletion. If the branch currently
|
||||
has a reflog then the reflog will also be deleted. Use -r together with -d
|
||||
to delete remote-tracking branches.
|
||||
has a reflog then the reflog will also be deleted.
|
||||
|
||||
Use -r together with -d to delete remote-tracking branches. Note, that it
|
||||
only makes sense to delete remote-tracking branches if they no longer exist
|
||||
in remote repository or if gitlink:git-fetch[1] was configured not to fetch
|
||||
them again. See also 'prune' subcommand of gitlink:git-remote[1] for way to
|
||||
clean up all obsolete remote-tracking branches.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-d::
|
||||
Delete a branch. The branch must be fully merged.
|
||||
Delete a branch. The branch must be fully merged in HEAD.
|
||||
|
||||
-D::
|
||||
Delete a branch irrespective of its index status.
|
||||
Delete a branch irrespective of its merged status.
|
||||
|
||||
-l::
|
||||
Create the branch's reflog. This activates recording of
|
||||
@@ -153,9 +158,11 @@ $ git branch -d -r origin/todo origin/html origin/man <1>
|
||||
$ git branch -D test <2>
|
||||
------------
|
||||
+
|
||||
<1> Delete remote-tracking branches "todo", "html", "man"
|
||||
<2> Delete "test" branch even if the "master" branch does not have all
|
||||
commits from test branch.
|
||||
<1> Delete remote-tracking branches "todo", "html", "man". Next 'fetch' or
|
||||
'pull' will create them again unless you configure them not to. See
|
||||
gitlink:git-fetch[1].
|
||||
<2> Delete "test" branch even if the "master" branch (or whichever branch is
|
||||
currently checked out) does not have all commits from test branch.
|
||||
|
||||
|
||||
Notes
|
||||
|
||||
@@ -12,8 +12,7 @@ SYNOPSIS
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Lists the references the remote repository has, and optionally
|
||||
stores them in the local repository under the same name.
|
||||
This command is deprecated; use `git-ls-remote` instead.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
@@ -63,6 +63,14 @@ the remote repository.
|
||||
Instead of naming each ref to push, specifies that all
|
||||
refs under `$GIT_DIR/refs/heads/` be pushed.
|
||||
|
||||
\--mirror::
|
||||
Instead of naming each ref to push, specifies that all
|
||||
refs under `$GIT_DIR/refs/heads/` and `$GIT_DIR/refs/tags/`
|
||||
be mirrored to the remote repository. Newly created local
|
||||
refs will be pushed to the remote end, locally updated refs
|
||||
will be force updated on the remote end, and deleted refs
|
||||
will be removed from the remote end.
|
||||
|
||||
\--dry-run::
|
||||
Do everything except actually send the updates.
|
||||
|
||||
|
||||
@@ -56,11 +56,12 @@ $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
|
||||
The initial clone may be time-consuming for a large project, but you
|
||||
will only need to clone once.
|
||||
|
||||
The clone command creates a new directory named after the project
|
||||
("git" or "linux-2.6" in the examples above). After you cd into this
|
||||
The clone command creates a new directory named after the project ("git"
|
||||
or "linux-2.6" in the examples above). After you cd into this
|
||||
directory, you will see that it contains a copy of the project files,
|
||||
together with a special top-level directory named ".git", which
|
||||
contains all the information about the history of the project.
|
||||
called the <<def_working_tree,working tree>>, together with a special
|
||||
top-level directory named ".git", which contains all the information
|
||||
about the history of the project.
|
||||
|
||||
[[how-to-check-out]]
|
||||
How to check out a different version of a project
|
||||
@@ -71,8 +72,13 @@ of files. It stores the history as a compressed collection of
|
||||
interrelated snapshots of the project's contents. In git each such
|
||||
version is called a <<def_commit,commit>>.
|
||||
|
||||
A single git repository may contain multiple branches. It keeps track
|
||||
of them by keeping a list of <<def_head,heads>> which reference the
|
||||
Those snapshots aren't necessarily all arranged in a single line from
|
||||
oldest to newest; instead, work may simultaneously proceed along
|
||||
parallel lines of development, called <def_branch,branches>>, which may
|
||||
merge and diverge.
|
||||
|
||||
A single git repository can track development on multiple branches. It
|
||||
does this by keeping a list of <<def_head,heads>> which reference the
|
||||
latest commit on each branch; the gitlink:git-branch[1] command shows
|
||||
you the list of branch heads:
|
||||
|
||||
@@ -1410,8 +1416,8 @@ with the changes to be reverted, then you will be asked to fix
|
||||
conflicts manually, just as in the case of <<resolving-a-merge,
|
||||
resolving a merge>>.
|
||||
|
||||
[[fixing-a-mistake-by-editing-history]]
|
||||
Fixing a mistake by editing history
|
||||
[[fixing-a-mistake-by-rewriting-history]]
|
||||
Fixing a mistake by rewriting history
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If the problematic commit is the most recent commit, and you have not
|
||||
@@ -1434,7 +1440,7 @@ Again, you should never do this to a commit that may already have
|
||||
been merged into another branch; use gitlink:git-revert[1] instead in
|
||||
that case.
|
||||
|
||||
It is also possible to edit commits further back in the history, but
|
||||
It is also possible to replace commits further back in the history, but
|
||||
this is an advanced topic to be left for
|
||||
<<cleaning-up-history,another chapter>>.
|
||||
|
||||
@@ -1554,6 +1560,11 @@ This may be time-consuming. Unlike most other git operations (including
|
||||
git-gc when run without any options), it is not safe to prune while
|
||||
other git operations are in progress in the same repository.
|
||||
|
||||
If gitlink:git-fsck[1] complains about sha1 mismatches or missing
|
||||
objects, you may have a much more serious problem; your best option is
|
||||
probably restoring from backups. See
|
||||
<<recovering-from-repository-corruption>> for a detailed discussion.
|
||||
|
||||
[[recovering-lost-changes]]
|
||||
Recovering lost changes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -1923,15 +1934,9 @@ or just
|
||||
$ git push ssh://yourserver.com/~you/proj.git master
|
||||
-------------------------------------------------
|
||||
|
||||
As with git-fetch, git-push will complain if this does not result in
|
||||
a <<fast-forwards,fast forward>>. Normally this is a sign of
|
||||
something wrong. However, if you are sure you know what you're
|
||||
doing, you may force git-push to perform the update anyway by
|
||||
preceding the branch name by a plus sign:
|
||||
|
||||
-------------------------------------------------
|
||||
$ git push ssh://yourserver.com/~you/proj.git +master
|
||||
-------------------------------------------------
|
||||
As with git-fetch, git-push will complain if this does not result in a
|
||||
<<fast-forwards,fast forward>>; see the following section for details on
|
||||
handling this case.
|
||||
|
||||
Note that the target of a "push" is normally a
|
||||
<<def_bare_repository,bare>> repository. You can also push to a
|
||||
@@ -1959,6 +1964,52 @@ See the explanations of the remote.<name>.url, branch.<name>.remote,
|
||||
and remote.<name>.push options in gitlink:git-config[1] for
|
||||
details.
|
||||
|
||||
[[forcing-push]]
|
||||
What to do when a push fails
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a push would not result in a <<fast-forwards,fast forward>> of the
|
||||
remote branch, then it will fail with an error like:
|
||||
|
||||
-------------------------------------------------
|
||||
error: remote 'refs/heads/master' is not an ancestor of
|
||||
local 'refs/heads/master'.
|
||||
Maybe you are not up-to-date and need to pull first?
|
||||
error: failed to push to 'ssh://yourserver.com/~you/proj.git'
|
||||
-------------------------------------------------
|
||||
|
||||
This can happen, for example, if you:
|
||||
|
||||
- use `git reset --hard` to remove already-published commits, or
|
||||
- use `git commit --amend` to replace already-published commits
|
||||
(as in <<fixing-a-mistake-by-rewriting-history>>), or
|
||||
- use `git rebase` to rebase any already-published commits (as
|
||||
in <<using-git-rebase>>).
|
||||
|
||||
You may force git-push to perform the update anyway by preceding the
|
||||
branch name with a plus sign:
|
||||
|
||||
-------------------------------------------------
|
||||
$ git push ssh://yourserver.com/~you/proj.git +master
|
||||
-------------------------------------------------
|
||||
|
||||
Normally whenever a branch head in a public repository is modified, it
|
||||
is modified to point to a descendent of the commit that it pointed to
|
||||
before. By forcing a push in this situation, you break that convention.
|
||||
(See <<problems-with-rewriting-history>>.)
|
||||
|
||||
Nevertheless, this is a common practice for people that need a simple
|
||||
way to publish a work-in-progress patch series, and it is an acceptable
|
||||
compromise as long as you warn other developers that this is how you
|
||||
intend to manage the branch.
|
||||
|
||||
It's also possible for a push to fail in this way when other people have
|
||||
the right to push to the same repository. In that case, the correct
|
||||
solution is to retry the push after first updating your work by either a
|
||||
pull or a fetch followed by a rebase; see the
|
||||
<<setting-up-a-shared-repository,next section>> and
|
||||
link:cvs-migration.html[git for CVS users] for more.
|
||||
|
||||
[[setting-up-a-shared-repository]]
|
||||
Setting up a shared repository
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -2426,11 +2477,11 @@ return mywork to the state it had before you started the rebase:
|
||||
$ git rebase --abort
|
||||
-------------------------------------------------
|
||||
|
||||
[[modifying-one-commit]]
|
||||
Modifying a single commit
|
||||
[[rewriting-one-commit]]
|
||||
Rewriting a single commit
|
||||
-------------------------
|
||||
|
||||
We saw in <<fixing-a-mistake-by-editing-history>> that you can replace the
|
||||
We saw in <<fixing-a-mistake-by-rewriting-history>> that you can replace the
|
||||
most recent commit using
|
||||
|
||||
-------------------------------------------------
|
||||
@@ -2440,8 +2491,10 @@ $ git commit --amend
|
||||
which will replace the old commit by a new commit incorporating your
|
||||
changes, giving you a chance to edit the old commit message first.
|
||||
|
||||
You can also use a combination of this and gitlink:git-rebase[1] to edit
|
||||
commits further back in your history. First, tag the problematic commit with
|
||||
You can also use a combination of this and gitlink:git-rebase[1] to
|
||||
replace a commit further back in your history and recreate the
|
||||
intervening changes on top of it. First, tag the problematic commit
|
||||
with
|
||||
|
||||
-------------------------------------------------
|
||||
$ git tag bad mywork~5
|
||||
@@ -3172,6 +3225,127 @@ confusing and scary messages, but it won't actually do anything bad. In
|
||||
contrast, running "git prune" while somebody is actively changing the
|
||||
repository is a *BAD* idea).
|
||||
|
||||
[[recovering-from-repository-corruption]]
|
||||
Recovering from repository corruption
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
By design, git treats data trusted to it with caution. However, even in
|
||||
the absence of bugs in git itself, it is still possible that hardware or
|
||||
operating system errors could corrupt data.
|
||||
|
||||
The first defense against such problems is backups. You can back up a
|
||||
git directory using clone, or just using cp, tar, or any other backup
|
||||
mechanism.
|
||||
|
||||
As a last resort, you can search for the corrupted objects and attempt
|
||||
to replace them by hand. Back up your repository before attempting this
|
||||
in case you corrupt things even more in the process.
|
||||
|
||||
We'll assume that the problem is a single missing or corrupted blob,
|
||||
which is sometimes a solveable problem. (Recovering missing trees and
|
||||
especially commits is *much* harder).
|
||||
|
||||
Before starting, verify that there is corruption, and figure out where
|
||||
it is with gitlink:git-fsck[1]; this may be time-consuming.
|
||||
|
||||
Assume the output looks like this:
|
||||
|
||||
------------------------------------------------
|
||||
$ git-fsck --full
|
||||
broken link from tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
|
||||
to blob 4b9458b3786228369c63936db65827de3cc06200
|
||||
missing blob 4b9458b3786228369c63936db65827de3cc06200
|
||||
------------------------------------------------
|
||||
|
||||
(Typically there will be some "dangling object" messages too, but they
|
||||
aren't interesting.)
|
||||
|
||||
Now you know that blob 4b9458b3 is missing, and that the tree 2d9263c6
|
||||
points to it. If you could find just one copy of that missing blob
|
||||
object, possibly in some other repository, you could move it into
|
||||
.git/objects/4b/9458b3... and be done. Suppose you can't. You can
|
||||
still examine the tree that pointed to it with gitlink:git-ls-tree[1],
|
||||
which might output something like:
|
||||
|
||||
------------------------------------------------
|
||||
$ git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
|
||||
100644 blob 8d14531846b95bfa3564b58ccfb7913a034323b8 .gitignore
|
||||
100644 blob ebf9bf84da0aab5ed944264a5db2a65fe3a3e883 .mailmap
|
||||
100644 blob ca442d313d86dc67e0a2e5d584b465bd382cbf5c COPYING
|
||||
...
|
||||
100644 blob 4b9458b3786228369c63936db65827de3cc06200 myfile
|
||||
...
|
||||
------------------------------------------------
|
||||
|
||||
So now you know that the missing blob was the data for a file named
|
||||
"myfile". And chances are you can also identify the directory--let's
|
||||
say it's in "somedirectory". If you're lucky the missing copy might be
|
||||
the same as the copy you have checked out in your working tree at
|
||||
"somedirectory/myfile"; you can test whether that's right with
|
||||
gitlink:git-hash-object[1]:
|
||||
|
||||
------------------------------------------------
|
||||
$ git hash-object -w somedirectory/myfile
|
||||
------------------------------------------------
|
||||
|
||||
which will create and store a blob object with the contents of
|
||||
somedirectory/myfile, and output the sha1 of that object. if you're
|
||||
extremely lucky it might be 4b9458b3786228369c63936db65827de3cc06200, in
|
||||
which case you've guessed right, and the corruption is fixed!
|
||||
|
||||
Otherwise, you need more information. How do you tell which version of
|
||||
the file has been lost?
|
||||
|
||||
The easiest way to do this is with:
|
||||
|
||||
------------------------------------------------
|
||||
$ git log --raw --all --full-history -- somedirectory/myfile
|
||||
------------------------------------------------
|
||||
|
||||
Because you're asking for raw output, you'll now get something like
|
||||
|
||||
------------------------------------------------
|
||||
commit abc
|
||||
Author:
|
||||
Date:
|
||||
...
|
||||
:100644 100644 4b9458b... newsha... M somedirectory/myfile
|
||||
|
||||
|
||||
commit xyz
|
||||
Author:
|
||||
Date:
|
||||
|
||||
...
|
||||
:100644 100644 oldsha... 4b9458b... M somedirectory/myfile
|
||||
------------------------------------------------
|
||||
|
||||
This tells you that the immediately preceding version of the file was
|
||||
"newsha", and that the immediately following version was "oldsha".
|
||||
You also know the commit messages that went with the change from oldsha
|
||||
to 4b9458b and with the change from 4b9458b to newsha.
|
||||
|
||||
If you've been committing small enough changes, you may now have a good
|
||||
shot at reconstructing the contents of the in-between state 4b9458b.
|
||||
|
||||
If you can do that, you can now recreate the missing object with
|
||||
|
||||
------------------------------------------------
|
||||
$ git hash-object -w <recreated-file>
|
||||
------------------------------------------------
|
||||
|
||||
and your repository is good again!
|
||||
|
||||
(Btw, you could have ignored the fsck, and started with doing a
|
||||
|
||||
------------------------------------------------
|
||||
$ git log --raw --all
|
||||
------------------------------------------------
|
||||
|
||||
and just looked for the sha of the missing object (4b9458b..) in that
|
||||
whole thing. It's up to you - git does *have* a lot of information, it is
|
||||
just missing one particular blob version.
|
||||
|
||||
[[the-index]]
|
||||
The index
|
||||
-----------
|
||||
@@ -3533,7 +3707,7 @@ should use the `--remove` and `--add` flags respectively.
|
||||
NOTE! A `--remove` flag does 'not' mean that subsequent filenames will
|
||||
necessarily be removed: if the files still exist in your directory
|
||||
structure, the index will be updated with their new status, not
|
||||
removed. The only thing `--remove` means is that update-cache will be
|
||||
removed. The only thing `--remove` means is that update-index will be
|
||||
considering a removed file to be a valid thing, and if the file really
|
||||
does not exist any more, it will update the index accordingly.
|
||||
|
||||
@@ -4382,4 +4556,7 @@ Write a chapter on using plumbing and writing scripts.
|
||||
|
||||
Alternates, clone -reference, etc.
|
||||
|
||||
git unpack-objects -r for recovery
|
||||
More on recovery from repository corruption. See:
|
||||
http://marc.theaimsgroup.com/?l=git&m=117263864820799&w=2
|
||||
http://marc.theaimsgroup.com/?l=git&m=117147855503798&w=2
|
||||
http://marc.theaimsgroup.com/?l=git&m=117147855503798&w=2
|
||||
|
||||
10
Makefile
10
Makefile
@@ -114,7 +114,7 @@ all::
|
||||
# times (my ext3 doesn't).
|
||||
#
|
||||
# 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.
|
||||
# change being considered an inode change from the update-index perspective.
|
||||
#
|
||||
# Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
|
||||
#
|
||||
@@ -216,8 +216,7 @@ BASIC_LDFLAGS =
|
||||
|
||||
SCRIPT_SH = \
|
||||
git-bisect.sh git-checkout.sh \
|
||||
git-clean.sh git-clone.sh git-commit.sh \
|
||||
git-ls-remote.sh \
|
||||
git-clone.sh git-commit.sh \
|
||||
git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
|
||||
git-pull.sh git-rebase.sh git-rebase--interactive.sh \
|
||||
git-repack.sh git-request-pull.sh \
|
||||
@@ -245,7 +244,7 @@ PROGRAMS = \
|
||||
git-hash-object$X git-index-pack$X \
|
||||
git-fast-import$X \
|
||||
git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
|
||||
git-peek-remote$X git-receive-pack$X \
|
||||
git-receive-pack$X \
|
||||
git-send-pack$X git-shell$X \
|
||||
git-show-index$X \
|
||||
git-unpack-file$X \
|
||||
@@ -332,6 +331,7 @@ BUILTIN_OBJS = \
|
||||
builtin-check-attr.o \
|
||||
builtin-checkout-index.o \
|
||||
builtin-check-ref-format.o \
|
||||
builtin-clean.o \
|
||||
builtin-commit-tree.o \
|
||||
builtin-count-objects.o \
|
||||
builtin-describe.o \
|
||||
@@ -351,6 +351,7 @@ BUILTIN_OBJS = \
|
||||
builtin-log.o \
|
||||
builtin-ls-files.o \
|
||||
builtin-ls-tree.o \
|
||||
builtin-ls-remote.o \
|
||||
builtin-mailinfo.o \
|
||||
builtin-mailsplit.o \
|
||||
builtin-merge-base.o \
|
||||
@@ -364,6 +365,7 @@ BUILTIN_OBJS = \
|
||||
builtin-push.o \
|
||||
builtin-read-tree.o \
|
||||
builtin-reflog.o \
|
||||
builtin-send-pack.o \
|
||||
builtin-config.o \
|
||||
builtin-rerere.o \
|
||||
builtin-reset.o \
|
||||
|
||||
153
builtin-clean.c
Normal file
153
builtin-clean.c
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* "git clean" builtin command
|
||||
*
|
||||
* Copyright (C) 2007 Shawn Bohrer
|
||||
*
|
||||
* Based on git-clean.sh by Pavel Roskin
|
||||
*/
|
||||
|
||||
#include "builtin.h"
|
||||
#include "cache.h"
|
||||
#include "dir.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
static int force = -1; /* unset */
|
||||
|
||||
static const char *const builtin_clean_usage[] = {
|
||||
"git-clean [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int git_clean_config(const char *var, const char *value)
|
||||
{
|
||||
if (!strcmp(var, "clean.requireforce"))
|
||||
force = !git_config_bool(var, value);
|
||||
return git_default_config(var, value);
|
||||
}
|
||||
|
||||
int cmd_clean(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int j;
|
||||
int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
|
||||
int ignored_only = 0, baselen = 0, config_set = 0;
|
||||
struct strbuf directory;
|
||||
struct dir_struct dir;
|
||||
const char *path, *base;
|
||||
static const char **pathspec;
|
||||
struct option options[] = {
|
||||
OPT__QUIET(&quiet),
|
||||
OPT__DRY_RUN(&show_only),
|
||||
OPT_BOOLEAN('f', NULL, &force, "force"),
|
||||
OPT_BOOLEAN('d', NULL, &remove_directories,
|
||||
"remove whole directories"),
|
||||
OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
|
||||
OPT_BOOLEAN('X', NULL, &ignored_only,
|
||||
"remove only ignored files"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
git_config(git_clean_config);
|
||||
if (force < 0)
|
||||
force = 0;
|
||||
else
|
||||
config_set = 1;
|
||||
|
||||
argc = parse_options(argc, argv, options, builtin_clean_usage, 0);
|
||||
|
||||
memset(&dir, 0, sizeof(dir));
|
||||
if (ignored_only)
|
||||
dir.show_ignored = 1;
|
||||
|
||||
if (ignored && ignored_only)
|
||||
die("-x and -X cannot be used together");
|
||||
|
||||
if (!show_only && !force)
|
||||
die("clean.requireForce%s set and -n or -f not given; "
|
||||
"refusing to clean", config_set ? "" : " not");
|
||||
|
||||
dir.show_other_directories = 1;
|
||||
|
||||
if (!ignored)
|
||||
setup_standard_excludes(&dir);
|
||||
|
||||
pathspec = get_pathspec(prefix, argv);
|
||||
read_cache();
|
||||
|
||||
/*
|
||||
* Calculate common prefix for the pathspec, and
|
||||
* use that to optimize the directory walk
|
||||
*/
|
||||
baselen = common_prefix(pathspec);
|
||||
path = ".";
|
||||
base = "";
|
||||
if (baselen)
|
||||
path = base = xmemdupz(*pathspec, baselen);
|
||||
read_directory(&dir, path, base, baselen, pathspec);
|
||||
strbuf_init(&directory, 0);
|
||||
|
||||
for (j = 0; j < dir.nr; ++j) {
|
||||
struct dir_entry *ent = dir.entries[j];
|
||||
int len, pos, specs;
|
||||
struct cache_entry *ce;
|
||||
struct stat st;
|
||||
char *seen;
|
||||
|
||||
/*
|
||||
* Remove the '/' at the end that directory
|
||||
* walking adds for directory entries.
|
||||
*/
|
||||
len = ent->len;
|
||||
if (len && ent->name[len-1] == '/')
|
||||
len--;
|
||||
pos = cache_name_pos(ent->name, len);
|
||||
if (0 <= pos)
|
||||
continue; /* exact match */
|
||||
pos = -pos - 1;
|
||||
if (pos < active_nr) {
|
||||
ce = active_cache[pos];
|
||||
if (ce_namelen(ce) == len &&
|
||||
!memcmp(ce->name, ent->name, len))
|
||||
continue; /* Yup, this one exists unmerged */
|
||||
}
|
||||
|
||||
if (!lstat(ent->name, &st) && (S_ISDIR(st.st_mode))) {
|
||||
int matched_path = 0;
|
||||
strbuf_addstr(&directory, ent->name);
|
||||
if (pathspec) {
|
||||
for (specs =0; pathspec[specs]; ++specs)
|
||||
/* nothing */;
|
||||
seen = xcalloc(specs, 1);
|
||||
/* Check if directory was explictly passed as
|
||||
* pathspec. If so we want to remove it */
|
||||
if (match_pathspec(pathspec, ent->name, ent->len,
|
||||
baselen, seen))
|
||||
matched_path = 1;
|
||||
free(seen);
|
||||
}
|
||||
if (show_only && (remove_directories || matched_path)) {
|
||||
printf("Would remove %s\n", directory.buf);
|
||||
} else if (quiet && (remove_directories || matched_path)) {
|
||||
remove_dir_recursively(&directory, 0);
|
||||
} else if (remove_directories || matched_path) {
|
||||
printf("Removing %s\n", directory.buf);
|
||||
remove_dir_recursively(&directory, 0);
|
||||
} else if (show_only) {
|
||||
printf("Would not remove %s\n", directory.buf);
|
||||
} else {
|
||||
printf("Not removing %s\n", directory.buf);
|
||||
}
|
||||
strbuf_reset(&directory);
|
||||
} else {
|
||||
if (show_only) {
|
||||
printf("Would remove %s\n", ent->name);
|
||||
continue;
|
||||
} else if (!quiet) {
|
||||
printf("Removing %s\n", ent->name);
|
||||
}
|
||||
unlink(ent->name);
|
||||
}
|
||||
}
|
||||
|
||||
strbuf_release(&directory);
|
||||
return 0;
|
||||
}
|
||||
@@ -511,10 +511,14 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
|
||||
if (!strcmp("append-fetch-head", argv[1])) {
|
||||
int result;
|
||||
FILE *fp;
|
||||
char *filename;
|
||||
|
||||
if (argc != 8)
|
||||
return error("append-fetch-head takes 6 args");
|
||||
fp = fopen(git_path("FETCH_HEAD"), "a");
|
||||
filename = git_path("FETCH_HEAD");
|
||||
fp = fopen(filename, "a");
|
||||
if (!fp)
|
||||
return error("cannot open %s: %s\n", filename, strerror(errno));
|
||||
result = append_fetch_head(fp, argv[2], argv[3],
|
||||
argv[4], argv[5],
|
||||
argv[6], !!argv[7][0],
|
||||
@@ -525,10 +529,14 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
|
||||
if (!strcmp("native-store", argv[1])) {
|
||||
int result;
|
||||
FILE *fp;
|
||||
char *filename;
|
||||
|
||||
if (argc != 5)
|
||||
return error("fetch-native-store takes 3 args");
|
||||
fp = fopen(git_path("FETCH_HEAD"), "a");
|
||||
filename = git_path("FETCH_HEAD");
|
||||
fp = fopen(filename, "a");
|
||||
if (!fp)
|
||||
return error("cannot open %s: %s\n", filename, strerror(errno));
|
||||
result = fetch_native_store(fp, argv[2], argv[3], argv[4],
|
||||
verbose, force);
|
||||
fclose(fp);
|
||||
|
||||
@@ -31,7 +31,7 @@ static void unlock_pack_on_signal(int signo)
|
||||
}
|
||||
|
||||
static void add_merge_config(struct ref **head,
|
||||
struct ref *remote_refs,
|
||||
const struct ref *remote_refs,
|
||||
struct branch *branch,
|
||||
struct ref ***tail)
|
||||
{
|
||||
@@ -79,7 +79,7 @@ static struct ref *get_ref_map(struct transport *transport,
|
||||
struct ref *ref_map = NULL;
|
||||
struct ref **tail = &ref_map;
|
||||
|
||||
struct ref *remote_refs = transport_get_remote_refs(transport);
|
||||
const struct ref *remote_refs = transport_get_remote_refs(transport);
|
||||
|
||||
if (ref_count || tags) {
|
||||
for (i = 0; i < ref_count; i++) {
|
||||
@@ -255,7 +255,7 @@ static int update_local_ref(struct ref *ref,
|
||||
}
|
||||
}
|
||||
|
||||
static void store_updated_refs(const char *url, struct ref *ref_map)
|
||||
static int store_updated_refs(const char *url, struct ref *ref_map)
|
||||
{
|
||||
FILE *fp;
|
||||
struct commit *commit;
|
||||
@@ -263,8 +263,11 @@ static void store_updated_refs(const char *url, struct ref *ref_map)
|
||||
char note[1024];
|
||||
const char *what, *kind;
|
||||
struct ref *rm;
|
||||
char *filename = git_path("FETCH_HEAD");
|
||||
|
||||
fp = fopen(git_path("FETCH_HEAD"), "a");
|
||||
fp = fopen(filename, "a");
|
||||
if (!fp)
|
||||
return error("cannot open %s: %s\n", filename, strerror(errno));
|
||||
for (rm = ref_map; rm; rm = rm->next) {
|
||||
struct ref *ref = NULL;
|
||||
|
||||
@@ -335,6 +338,7 @@ static void store_updated_refs(const char *url, struct ref *ref_map)
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -404,7 +408,7 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
|
||||
if (ret)
|
||||
ret = transport_fetch_refs(transport, ref_map);
|
||||
if (!ret)
|
||||
store_updated_refs(transport->url, ref_map);
|
||||
ret |= store_updated_refs(transport->url, ref_map);
|
||||
transport_unlock_pack(transport);
|
||||
return ret;
|
||||
}
|
||||
@@ -424,12 +428,12 @@ static struct ref *find_non_local_tags(struct transport *transport,
|
||||
struct path_list new_refs = { NULL, 0, 0, 1 };
|
||||
char *ref_name;
|
||||
int ref_name_len;
|
||||
unsigned char *ref_sha1;
|
||||
struct ref *tag_ref;
|
||||
const unsigned char *ref_sha1;
|
||||
const struct ref *tag_ref;
|
||||
struct ref *rm = NULL;
|
||||
struct ref *ref_map = NULL;
|
||||
struct ref **tail = &ref_map;
|
||||
struct ref *ref;
|
||||
const struct ref *ref;
|
||||
|
||||
for_each_ref(add_existing, &existing_refs);
|
||||
for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
|
||||
@@ -487,8 +491,13 @@ static int do_fetch(struct transport *transport,
|
||||
die("Don't know how to fetch from %s", transport->url);
|
||||
|
||||
/* if not appending, truncate FETCH_HEAD */
|
||||
if (!append)
|
||||
fclose(fopen(git_path("FETCH_HEAD"), "w"));
|
||||
if (!append) {
|
||||
char *filename = git_path("FETCH_HEAD");
|
||||
FILE *fp = fopen(filename, "w");
|
||||
if (!fp)
|
||||
return error("cannot open %s: %s\n", filename, strerror(errno));
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
|
||||
|
||||
|
||||
74
builtin-ls-remote.c
Normal file
74
builtin-ls-remote.c
Normal file
@@ -0,0 +1,74 @@
|
||||
#include "builtin.h"
|
||||
#include "cache.h"
|
||||
#include "transport.h"
|
||||
#include "remote.h"
|
||||
|
||||
static const char ls_remote_usage[] =
|
||||
"git-ls-remote [--upload-pack=<git-upload-pack>] [<host>:]<directory>";
|
||||
|
||||
int cmd_ls_remote(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i;
|
||||
const char *dest = NULL;
|
||||
int nongit = 0;
|
||||
unsigned flags = 0;
|
||||
const char *uploadpack = NULL;
|
||||
|
||||
struct remote *remote;
|
||||
struct transport *transport;
|
||||
const struct ref *ref;
|
||||
|
||||
setup_git_directory_gently(&nongit);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
|
||||
if (*arg == '-') {
|
||||
if (!prefixcmp(arg, "--upload-pack=")) {
|
||||
uploadpack = arg + 14;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--exec=")) {
|
||||
uploadpack = arg + 7;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--tags", arg)) {
|
||||
flags |= REF_TAGS;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--heads", arg)) {
|
||||
flags |= REF_HEADS;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--refs", arg)) {
|
||||
flags |= REF_NORMAL;
|
||||
continue;
|
||||
}
|
||||
usage(ls_remote_usage);
|
||||
}
|
||||
dest = arg;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!dest || i != argc - 1)
|
||||
usage(ls_remote_usage);
|
||||
|
||||
remote = nongit ? NULL : remote_get(dest);
|
||||
if (remote && !remote->url_nr)
|
||||
die("remote %s has no configured URL", dest);
|
||||
transport = transport_get(remote, remote ? remote->url[0] : dest);
|
||||
if (uploadpack != NULL)
|
||||
transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack);
|
||||
|
||||
ref = transport_get_remote_refs(transport);
|
||||
|
||||
if (!ref)
|
||||
return 1;
|
||||
|
||||
while (ref) {
|
||||
if (check_ref_type(ref, flags))
|
||||
printf("%s %s\n", sha1_to_hex(ref->old_sha1), ref->name);
|
||||
ref = ref->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "parse-options.h"
|
||||
|
||||
static const char * const push_usage[] = {
|
||||
"git-push [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]",
|
||||
"git-push [--all | --mirror] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]",
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -91,6 +91,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int flags = 0;
|
||||
int all = 0;
|
||||
int mirror = 0;
|
||||
int dry_run = 0;
|
||||
int force = 0;
|
||||
int tags = 0;
|
||||
@@ -100,6 +101,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
|
||||
OPT__VERBOSE(&verbose),
|
||||
OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
|
||||
OPT_BOOLEAN( 0 , "all", &all, "push all refs"),
|
||||
OPT_BOOLEAN( 0 , "mirror", &mirror, "mirror all refs"),
|
||||
OPT_BOOLEAN( 0 , "tags", &tags, "push tags"),
|
||||
OPT_BOOLEAN( 0 , "dry-run", &dry_run, "dry run"),
|
||||
OPT_BOOLEAN('f', "force", &force, "force updates"),
|
||||
@@ -121,13 +123,21 @@ int cmd_push(int argc, const char **argv, const char *prefix)
|
||||
add_refspec("refs/tags/*");
|
||||
if (all)
|
||||
flags |= TRANSPORT_PUSH_ALL;
|
||||
if (mirror)
|
||||
flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
|
||||
|
||||
if (argc > 0) {
|
||||
repo = argv[0];
|
||||
set_refspecs(argv + 1, argc - 1);
|
||||
}
|
||||
if ((flags & TRANSPORT_PUSH_ALL) && refspec)
|
||||
if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) && refspec)
|
||||
usage_with_options(push_usage, options);
|
||||
|
||||
if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
|
||||
(TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
|
||||
error("--all and --mirror are incompatible");
|
||||
usage_with_options(push_usage, options);
|
||||
}
|
||||
|
||||
return do_push(repo, flags);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ static const char * const cherry_pick_usage[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static int edit, no_replay, no_commit, needed_deref, mainline;
|
||||
static int edit, no_replay, no_commit, mainline;
|
||||
static enum { REVERT, CHERRY_PICK } action;
|
||||
static struct commit *commit;
|
||||
|
||||
@@ -66,7 +66,6 @@ static void parse_args(int argc, const char **argv)
|
||||
if (commit->object.type == OBJ_TAG) {
|
||||
commit = (struct commit *)
|
||||
deref_tag((struct object *)commit, arg, strlen(arg));
|
||||
needed_deref = 1;
|
||||
}
|
||||
if (commit->object.type != OBJ_COMMIT)
|
||||
die ("'%s' does not point to a commit", arg);
|
||||
@@ -333,17 +332,6 @@ static int revert_or_cherry_pick(int argc, const char **argv)
|
||||
add_to_msg(")\n");
|
||||
}
|
||||
}
|
||||
if (needed_deref) {
|
||||
add_to_msg("(original 'git ");
|
||||
add_to_msg(me);
|
||||
add_to_msg("' arguments: ");
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (i)
|
||||
add_to_msg(" ");
|
||||
add_to_msg(argv[i]);
|
||||
}
|
||||
add_to_msg(")\n");
|
||||
}
|
||||
|
||||
if (merge_recursive(sha1_to_hex(base->object.sha1),
|
||||
sha1_to_hex(head), "HEAD",
|
||||
|
||||
652
builtin-send-pack.c
Normal file
652
builtin-send-pack.c
Normal file
@@ -0,0 +1,652 @@
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
#include "tag.h"
|
||||
#include "refs.h"
|
||||
#include "pkt-line.h"
|
||||
#include "run-command.h"
|
||||
#include "remote.h"
|
||||
#include "send-pack.h"
|
||||
|
||||
static const char send_pack_usage[] =
|
||||
"git-send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
|
||||
" --all and explicit <ref> specification are mutually exclusive.";
|
||||
|
||||
static struct send_pack_args args = {
|
||||
/* .receivepack = */ "git-receive-pack",
|
||||
};
|
||||
|
||||
/*
|
||||
* Make a pack stream and spit it out into file descriptor fd
|
||||
*/
|
||||
static int pack_objects(int fd, struct ref *refs)
|
||||
{
|
||||
/*
|
||||
* The child becomes pack-objects --revs; we feed
|
||||
* the revision parameters to it via its stdin and
|
||||
* let its stdout go back to the other end.
|
||||
*/
|
||||
const char *argv[] = {
|
||||
"pack-objects",
|
||||
"--all-progress",
|
||||
"--revs",
|
||||
"--stdout",
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
struct child_process po;
|
||||
|
||||
if (args.use_thin_pack)
|
||||
argv[4] = "--thin";
|
||||
memset(&po, 0, sizeof(po));
|
||||
po.argv = argv;
|
||||
po.in = -1;
|
||||
po.out = fd;
|
||||
po.git_cmd = 1;
|
||||
if (start_command(&po))
|
||||
die("git-pack-objects failed (%s)", strerror(errno));
|
||||
|
||||
/*
|
||||
* We feed the pack-objects we just spawned with revision
|
||||
* parameters by writing to the pipe.
|
||||
*/
|
||||
while (refs) {
|
||||
char buf[42];
|
||||
|
||||
if (!is_null_sha1(refs->old_sha1) &&
|
||||
has_sha1_file(refs->old_sha1)) {
|
||||
memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
|
||||
buf[0] = '^';
|
||||
buf[41] = '\n';
|
||||
if (!write_or_whine(po.in, buf, 42,
|
||||
"send-pack: send refs"))
|
||||
break;
|
||||
}
|
||||
if (!is_null_sha1(refs->new_sha1)) {
|
||||
memcpy(buf, sha1_to_hex(refs->new_sha1), 40);
|
||||
buf[40] = '\n';
|
||||
if (!write_or_whine(po.in, buf, 41,
|
||||
"send-pack: send refs"))
|
||||
break;
|
||||
}
|
||||
refs = refs->next;
|
||||
}
|
||||
|
||||
if (finish_command(&po))
|
||||
return error("pack-objects died with strange error");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unmark_and_free(struct commit_list *list, unsigned int mark)
|
||||
{
|
||||
while (list) {
|
||||
struct commit_list *temp = list;
|
||||
temp->item->object.flags &= ~mark;
|
||||
list = temp->next;
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
|
||||
static int ref_newer(const unsigned char *new_sha1,
|
||||
const unsigned char *old_sha1)
|
||||
{
|
||||
struct object *o;
|
||||
struct commit *old, *new;
|
||||
struct commit_list *list, *used;
|
||||
int found = 0;
|
||||
|
||||
/* Both new and old must be commit-ish and new is descendant of
|
||||
* old. Otherwise we require --force.
|
||||
*/
|
||||
o = deref_tag(parse_object(old_sha1), NULL, 0);
|
||||
if (!o || o->type != OBJ_COMMIT)
|
||||
return 0;
|
||||
old = (struct commit *) o;
|
||||
|
||||
o = deref_tag(parse_object(new_sha1), NULL, 0);
|
||||
if (!o || o->type != OBJ_COMMIT)
|
||||
return 0;
|
||||
new = (struct commit *) o;
|
||||
|
||||
if (parse_commit(new) < 0)
|
||||
return 0;
|
||||
|
||||
used = list = NULL;
|
||||
commit_list_insert(new, &list);
|
||||
while (list) {
|
||||
new = pop_most_recent_commit(&list, 1);
|
||||
commit_list_insert(new, &used);
|
||||
if (new == old) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
unmark_and_free(list, 1);
|
||||
unmark_and_free(used, 1);
|
||||
return found;
|
||||
}
|
||||
|
||||
static struct ref *local_refs, **local_tail;
|
||||
static struct ref *remote_refs, **remote_tail;
|
||||
|
||||
static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
|
||||
{
|
||||
struct ref *ref;
|
||||
int len = strlen(refname) + 1;
|
||||
ref = xcalloc(1, sizeof(*ref) + len);
|
||||
hashcpy(ref->new_sha1, sha1);
|
||||
memcpy(ref->name, refname, len);
|
||||
*local_tail = ref;
|
||||
local_tail = &ref->next;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_local_heads(void)
|
||||
{
|
||||
local_tail = &local_refs;
|
||||
for_each_ref(one_local_ref, NULL);
|
||||
}
|
||||
|
||||
static int receive_status(int in, struct ref *refs)
|
||||
{
|
||||
struct ref *hint;
|
||||
char line[1000];
|
||||
int ret = 0;
|
||||
int len = packet_read_line(in, line, sizeof(line));
|
||||
if (len < 10 || memcmp(line, "unpack ", 7))
|
||||
return error("did not receive remote status");
|
||||
if (memcmp(line, "unpack ok\n", 10)) {
|
||||
char *p = line + strlen(line) - 1;
|
||||
if (*p == '\n')
|
||||
*p = '\0';
|
||||
error("unpack failed: %s", line + 7);
|
||||
ret = -1;
|
||||
}
|
||||
hint = NULL;
|
||||
while (1) {
|
||||
char *refname;
|
||||
char *msg;
|
||||
len = packet_read_line(in, line, sizeof(line));
|
||||
if (!len)
|
||||
break;
|
||||
if (len < 3 ||
|
||||
(memcmp(line, "ok ", 3) && memcmp(line, "ng ", 3))) {
|
||||
fprintf(stderr, "protocol error: %s\n", line);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
line[strlen(line)-1] = '\0';
|
||||
refname = line + 3;
|
||||
msg = strchr(refname, ' ');
|
||||
if (msg)
|
||||
*msg++ = '\0';
|
||||
|
||||
/* first try searching at our hint, falling back to all refs */
|
||||
if (hint)
|
||||
hint = find_ref_by_name(hint, refname);
|
||||
if (!hint)
|
||||
hint = find_ref_by_name(refs, refname);
|
||||
if (!hint) {
|
||||
warning("remote reported status on unknown ref: %s",
|
||||
refname);
|
||||
continue;
|
||||
}
|
||||
if (hint->status != REF_STATUS_EXPECTING_REPORT) {
|
||||
warning("remote reported status on unexpected ref: %s",
|
||||
refname);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line[0] == 'o' && line[1] == 'k')
|
||||
hint->status = REF_STATUS_OK;
|
||||
else {
|
||||
hint->status = REF_STATUS_REMOTE_REJECT;
|
||||
ret = -1;
|
||||
}
|
||||
if (msg)
|
||||
hint->remote_status = xstrdup(msg);
|
||||
/* start our next search from the next ref */
|
||||
hint = hint->next;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void update_tracking_ref(struct remote *remote, struct ref *ref)
|
||||
{
|
||||
struct refspec rs;
|
||||
|
||||
if (ref->status != REF_STATUS_OK)
|
||||
return;
|
||||
|
||||
rs.src = ref->name;
|
||||
rs.dst = NULL;
|
||||
|
||||
if (!remote_find_tracking(remote, &rs)) {
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
|
||||
if (ref->deletion) {
|
||||
if (delete_ref(rs.dst, NULL))
|
||||
error("Failed to delete");
|
||||
} else
|
||||
update_ref("update by push", rs.dst,
|
||||
ref->new_sha1, NULL, 0, 0);
|
||||
free(rs.dst);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *prettify_ref(const struct ref *ref)
|
||||
{
|
||||
const char *name = ref->name;
|
||||
return name + (
|
||||
!prefixcmp(name, "refs/heads/") ? 11 :
|
||||
!prefixcmp(name, "refs/tags/") ? 10 :
|
||||
!prefixcmp(name, "refs/remotes/") ? 13 :
|
||||
0);
|
||||
}
|
||||
|
||||
#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
|
||||
|
||||
static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg)
|
||||
{
|
||||
fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
|
||||
if (from)
|
||||
fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to));
|
||||
else
|
||||
fputs(prettify_ref(to), stderr);
|
||||
if (msg) {
|
||||
fputs(" (", stderr);
|
||||
fputs(msg, stderr);
|
||||
fputc(')', stderr);
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
static const char *status_abbrev(unsigned char sha1[20])
|
||||
{
|
||||
const char *abbrev;
|
||||
abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV);
|
||||
return abbrev ? abbrev : sha1_to_hex(sha1);
|
||||
}
|
||||
|
||||
static void print_ok_ref_status(struct ref *ref)
|
||||
{
|
||||
if (ref->deletion)
|
||||
print_ref_status('-', "[deleted]", ref, NULL, NULL);
|
||||
else if (is_null_sha1(ref->old_sha1))
|
||||
print_ref_status('*',
|
||||
(!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" :
|
||||
"[new branch]"),
|
||||
ref, ref->peer_ref, NULL);
|
||||
else {
|
||||
char quickref[84];
|
||||
char type;
|
||||
const char *msg;
|
||||
|
||||
strcpy(quickref, status_abbrev(ref->old_sha1));
|
||||
if (ref->nonfastforward) {
|
||||
strcat(quickref, "...");
|
||||
type = '+';
|
||||
msg = "forced update";
|
||||
} else {
|
||||
strcat(quickref, "..");
|
||||
type = ' ';
|
||||
msg = NULL;
|
||||
}
|
||||
strcat(quickref, status_abbrev(ref->new_sha1));
|
||||
|
||||
print_ref_status(type, quickref, ref, ref->peer_ref, msg);
|
||||
}
|
||||
}
|
||||
|
||||
static int print_one_push_status(struct ref *ref, const char *dest, int count)
|
||||
{
|
||||
if (!count)
|
||||
fprintf(stderr, "To %s\n", dest);
|
||||
|
||||
switch(ref->status) {
|
||||
case REF_STATUS_NONE:
|
||||
print_ref_status('X', "[no match]", ref, NULL, NULL);
|
||||
break;
|
||||
case REF_STATUS_REJECT_NODELETE:
|
||||
print_ref_status('!', "[rejected]", ref, NULL,
|
||||
"remote does not support deleting refs");
|
||||
break;
|
||||
case REF_STATUS_UPTODATE:
|
||||
print_ref_status('=', "[up to date]", ref,
|
||||
ref->peer_ref, NULL);
|
||||
break;
|
||||
case REF_STATUS_REJECT_NONFASTFORWARD:
|
||||
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
|
||||
"non-fast forward");
|
||||
break;
|
||||
case REF_STATUS_REMOTE_REJECT:
|
||||
print_ref_status('!', "[remote rejected]", ref,
|
||||
ref->deletion ? NULL : ref->peer_ref,
|
||||
ref->remote_status);
|
||||
break;
|
||||
case REF_STATUS_EXPECTING_REPORT:
|
||||
print_ref_status('!', "[remote failure]", ref,
|
||||
ref->deletion ? NULL : ref->peer_ref,
|
||||
"remote failed to report status");
|
||||
break;
|
||||
case REF_STATUS_OK:
|
||||
print_ok_ref_status(ref);
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void print_push_status(const char *dest, struct ref *refs)
|
||||
{
|
||||
struct ref *ref;
|
||||
int n = 0;
|
||||
|
||||
if (args.verbose) {
|
||||
for (ref = refs; ref; ref = ref->next)
|
||||
if (ref->status == REF_STATUS_UPTODATE)
|
||||
n += print_one_push_status(ref, dest, n);
|
||||
}
|
||||
|
||||
for (ref = refs; ref; ref = ref->next)
|
||||
if (ref->status == REF_STATUS_OK)
|
||||
n += print_one_push_status(ref, dest, n);
|
||||
|
||||
for (ref = refs; ref; ref = ref->next) {
|
||||
if (ref->status != REF_STATUS_NONE &&
|
||||
ref->status != REF_STATUS_UPTODATE &&
|
||||
ref->status != REF_STATUS_OK)
|
||||
n += print_one_push_status(ref, dest, n);
|
||||
}
|
||||
}
|
||||
|
||||
static int refs_pushed(struct ref *ref)
|
||||
{
|
||||
for (; ref; ref = ref->next) {
|
||||
switch(ref->status) {
|
||||
case REF_STATUS_NONE:
|
||||
case REF_STATUS_UPTODATE:
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec)
|
||||
{
|
||||
struct ref *ref;
|
||||
int new_refs;
|
||||
int ask_for_status_report = 0;
|
||||
int allow_deleting_refs = 0;
|
||||
int expect_status_report = 0;
|
||||
int flags = MATCH_REFS_NONE;
|
||||
int ret;
|
||||
|
||||
if (args.send_all)
|
||||
flags |= MATCH_REFS_ALL;
|
||||
if (args.send_mirror)
|
||||
flags |= MATCH_REFS_MIRROR;
|
||||
|
||||
/* No funny business with the matcher */
|
||||
remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL);
|
||||
get_local_heads();
|
||||
|
||||
/* Does the other end support the reporting? */
|
||||
if (server_supports("report-status"))
|
||||
ask_for_status_report = 1;
|
||||
if (server_supports("delete-refs"))
|
||||
allow_deleting_refs = 1;
|
||||
|
||||
/* match them up */
|
||||
if (!remote_tail)
|
||||
remote_tail = &remote_refs;
|
||||
if (match_refs(local_refs, remote_refs, &remote_tail,
|
||||
nr_refspec, refspec, flags))
|
||||
return -1;
|
||||
|
||||
if (!remote_refs) {
|
||||
fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
|
||||
"Perhaps you should specify a branch such as 'master'.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, tell the other end!
|
||||
*/
|
||||
new_refs = 0;
|
||||
for (ref = remote_refs; ref; ref = ref->next) {
|
||||
const unsigned char *new_sha1;
|
||||
|
||||
if (!ref->peer_ref) {
|
||||
if (!args.send_mirror)
|
||||
continue;
|
||||
new_sha1 = null_sha1;
|
||||
}
|
||||
else
|
||||
new_sha1 = ref->peer_ref->new_sha1;
|
||||
|
||||
|
||||
ref->deletion = is_null_sha1(new_sha1);
|
||||
if (ref->deletion && !allow_deleting_refs) {
|
||||
ref->status = REF_STATUS_REJECT_NODELETE;
|
||||
continue;
|
||||
}
|
||||
if (!ref->deletion &&
|
||||
!hashcmp(ref->old_sha1, new_sha1)) {
|
||||
ref->status = REF_STATUS_UPTODATE;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* This part determines what can overwrite what.
|
||||
* The rules are:
|
||||
*
|
||||
* (0) you can always use --force or +A:B notation to
|
||||
* selectively force individual ref pairs.
|
||||
*
|
||||
* (1) if the old thing does not exist, it is OK.
|
||||
*
|
||||
* (2) if you do not have the old thing, you are not allowed
|
||||
* to overwrite it; you would not know what you are losing
|
||||
* otherwise.
|
||||
*
|
||||
* (3) if both new and old are commit-ish, and new is a
|
||||
* descendant of old, it is OK.
|
||||
*
|
||||
* (4) regardless of all of the above, removing :B is
|
||||
* always allowed.
|
||||
*/
|
||||
|
||||
ref->nonfastforward =
|
||||
!ref->deletion &&
|
||||
!is_null_sha1(ref->old_sha1) &&
|
||||
(!has_sha1_file(ref->old_sha1)
|
||||
|| !ref_newer(new_sha1, ref->old_sha1));
|
||||
|
||||
if (ref->nonfastforward && !ref->force && !args.force_update) {
|
||||
ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
|
||||
continue;
|
||||
}
|
||||
|
||||
hashcpy(ref->new_sha1, new_sha1);
|
||||
if (!ref->deletion)
|
||||
new_refs++;
|
||||
|
||||
if (!args.dry_run) {
|
||||
char *old_hex = sha1_to_hex(ref->old_sha1);
|
||||
char *new_hex = sha1_to_hex(ref->new_sha1);
|
||||
|
||||
if (ask_for_status_report) {
|
||||
packet_write(out, "%s %s %s%c%s",
|
||||
old_hex, new_hex, ref->name, 0,
|
||||
"report-status");
|
||||
ask_for_status_report = 0;
|
||||
expect_status_report = 1;
|
||||
}
|
||||
else
|
||||
packet_write(out, "%s %s %s",
|
||||
old_hex, new_hex, ref->name);
|
||||
}
|
||||
ref->status = expect_status_report ?
|
||||
REF_STATUS_EXPECTING_REPORT :
|
||||
REF_STATUS_OK;
|
||||
}
|
||||
|
||||
packet_flush(out);
|
||||
if (new_refs && !args.dry_run) {
|
||||
if (pack_objects(out, remote_refs) < 0) {
|
||||
close(out);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
close(out);
|
||||
|
||||
if (expect_status_report)
|
||||
ret = receive_status(in, remote_refs);
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
print_push_status(dest, remote_refs);
|
||||
|
||||
if (!args.dry_run && remote) {
|
||||
for (ref = remote_refs; ref; ref = ref->next)
|
||||
update_tracking_ref(remote, ref);
|
||||
}
|
||||
|
||||
if (!refs_pushed(remote_refs))
|
||||
fprintf(stderr, "Everything up-to-date\n");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
for (ref = remote_refs; ref; ref = ref->next) {
|
||||
switch (ref->status) {
|
||||
case REF_STATUS_NONE:
|
||||
case REF_STATUS_UPTODATE:
|
||||
case REF_STATUS_OK:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void verify_remote_names(int nr_heads, const char **heads)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_heads; i++) {
|
||||
const char *remote = strchr(heads[i], ':');
|
||||
|
||||
remote = remote ? (remote + 1) : heads[i];
|
||||
switch (check_ref_format(remote)) {
|
||||
case 0: /* ok */
|
||||
case -2: /* ok but a single level -- that is fine for
|
||||
* a match pattern.
|
||||
*/
|
||||
case -3: /* ok but ends with a pattern-match character */
|
||||
continue;
|
||||
}
|
||||
die("remote part of refspec is not a valid name in %s",
|
||||
heads[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int cmd_send_pack(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i, nr_heads = 0;
|
||||
const char **heads = NULL;
|
||||
const char *remote_name = NULL;
|
||||
struct remote *remote = NULL;
|
||||
const char *dest = NULL;
|
||||
|
||||
argv++;
|
||||
for (i = 1; i < argc; i++, argv++) {
|
||||
const char *arg = *argv;
|
||||
|
||||
if (*arg == '-') {
|
||||
if (!prefixcmp(arg, "--receive-pack=")) {
|
||||
args.receivepack = arg + 15;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--exec=")) {
|
||||
args.receivepack = arg + 7;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--remote=")) {
|
||||
remote_name = arg + 9;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--all")) {
|
||||
args.send_all = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--dry-run")) {
|
||||
args.dry_run = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--mirror")) {
|
||||
args.send_mirror = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--force")) {
|
||||
args.force_update = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--verbose")) {
|
||||
args.verbose = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--thin")) {
|
||||
args.use_thin_pack = 1;
|
||||
continue;
|
||||
}
|
||||
usage(send_pack_usage);
|
||||
}
|
||||
if (!dest) {
|
||||
dest = arg;
|
||||
continue;
|
||||
}
|
||||
heads = (const char **) argv;
|
||||
nr_heads = argc - i;
|
||||
break;
|
||||
}
|
||||
if (!dest)
|
||||
usage(send_pack_usage);
|
||||
/*
|
||||
* --all and --mirror are incompatible; neither makes sense
|
||||
* with any refspecs.
|
||||
*/
|
||||
if ((heads && (args.send_all || args.send_mirror)) ||
|
||||
(args.send_all && args.send_mirror))
|
||||
usage(send_pack_usage);
|
||||
|
||||
if (remote_name) {
|
||||
remote = remote_get(remote_name);
|
||||
if (!remote_has_url(remote, dest)) {
|
||||
die("Destination %s is not a uri for %s",
|
||||
dest, remote_name);
|
||||
}
|
||||
}
|
||||
|
||||
return send_pack(&args, dest, remote, nr_heads, heads);
|
||||
}
|
||||
|
||||
int send_pack(struct send_pack_args *my_args,
|
||||
const char *dest, struct remote *remote,
|
||||
int nr_heads, const char **heads)
|
||||
{
|
||||
int fd[2], ret;
|
||||
struct child_process *conn;
|
||||
|
||||
memcpy(&args, my_args, sizeof(args));
|
||||
|
||||
verify_remote_names(nr_heads, heads);
|
||||
|
||||
conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0);
|
||||
ret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads);
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
ret |= finish_connect(conn);
|
||||
return !!ret;
|
||||
}
|
||||
@@ -24,6 +24,7 @@ extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_cherry(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_clean(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_describe(int argc, const char **argv, const char *prefix);
|
||||
@@ -48,6 +49,7 @@ extern int cmd_log(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
|
||||
@@ -70,6 +72,7 @@ extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_revert(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_rm(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_runstatus(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_send_pack(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_shortlog(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_show(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
|
||||
|
||||
20
cache.h
20
cache.h
@@ -515,8 +515,20 @@ struct ref {
|
||||
struct ref *next;
|
||||
unsigned char old_sha1[20];
|
||||
unsigned char new_sha1[20];
|
||||
unsigned char force;
|
||||
unsigned char merge;
|
||||
unsigned int force:1,
|
||||
merge:1,
|
||||
nonfastforward:1,
|
||||
deletion:1;
|
||||
enum {
|
||||
REF_STATUS_NONE = 0,
|
||||
REF_STATUS_OK,
|
||||
REF_STATUS_REJECT_NONFASTFORWARD,
|
||||
REF_STATUS_REJECT_NODELETE,
|
||||
REF_STATUS_UPTODATE,
|
||||
REF_STATUS_REMOTE_REJECT,
|
||||
REF_STATUS_EXPECTING_REPORT,
|
||||
} status;
|
||||
char *remote_status;
|
||||
struct ref *peer_ref; /* when renaming */
|
||||
char name[FLEX_ARRAY]; /* more */
|
||||
};
|
||||
@@ -525,8 +537,10 @@ struct ref {
|
||||
#define REF_HEADS (1u << 1)
|
||||
#define REF_TAGS (1u << 2)
|
||||
|
||||
extern struct ref *find_ref_by_name(struct ref *list, const char *name);
|
||||
|
||||
#define CONNECT_VERBOSE (1u << 0)
|
||||
extern struct child_process *git_connect(int fd[2], char *url, const char *prog, int flags);
|
||||
extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
|
||||
extern int finish_connect(struct child_process *conn);
|
||||
extern int path_match(const char *path, int nr, char **match);
|
||||
extern int get_ack(int fd, unsigned char *result_sha1);
|
||||
|
||||
@@ -978,8 +978,8 @@ int setitimer(int type, struct itimerval *in, struct itimerval *out)
|
||||
is_timeval_eq(&in->it_interval, &zero))
|
||||
return 0;
|
||||
|
||||
timer_interval = in->it_interval.tv_sec * 1000 + in->it_interval.tv_usec / 1000;
|
||||
one_shot = is_timeval_eq(&in->it_value, &zero);
|
||||
timer_interval = in->it_value.tv_sec * 1000 + in->it_value.tv_usec / 1000;
|
||||
one_shot = is_timeval_eq(&in->it_interval, &zero);
|
||||
if (!atexit_done) {
|
||||
atexit(stop_timer_thread);
|
||||
atexit_done = 1;
|
||||
|
||||
@@ -415,7 +415,7 @@ GIT_PARSE_WITH(iconv))
|
||||
# times (my ext3 doesn't).
|
||||
#
|
||||
# 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.
|
||||
# change being considered an inode change from the update-index perspective.
|
||||
|
||||
|
||||
## Output files
|
||||
|
||||
10
connect.c
10
connect.c
@@ -36,6 +36,11 @@ static int check_ref(const char *name, int len, unsigned int flags)
|
||||
return !(flags & ~REF_NORMAL);
|
||||
}
|
||||
|
||||
int check_ref_type(const struct ref *ref, int flags)
|
||||
{
|
||||
return check_ref(ref->name, strlen(ref->name), flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read all the refs from the other end
|
||||
*/
|
||||
@@ -476,9 +481,10 @@ char *get_port(char *host)
|
||||
*
|
||||
* If it returns, the connect is successful; it just dies on errors.
|
||||
*/
|
||||
struct child_process *git_connect(int fd[2], char *url,
|
||||
struct child_process *git_connect(int fd[2], const char *url_orig,
|
||||
const char *prog, int flags)
|
||||
{
|
||||
char *url = xstrdup(url_orig);
|
||||
char *host, *path = url;
|
||||
char *end;
|
||||
int c;
|
||||
@@ -569,6 +575,7 @@ struct child_process *git_connect(int fd[2], char *url,
|
||||
prog, path, 0,
|
||||
target_host, 0);
|
||||
free(target_host);
|
||||
free(url);
|
||||
if (free_path)
|
||||
free(path);
|
||||
return NULL;
|
||||
@@ -620,6 +627,7 @@ struct child_process *git_connect(int fd[2], char *url,
|
||||
fd[0] = conn->out; /* read from child's stdout */
|
||||
fd[1] = conn->in; /* write to child's stdin */
|
||||
strbuf_release(&cmd);
|
||||
free(url);
|
||||
if (free_path)
|
||||
free(path);
|
||||
return conn;
|
||||
|
||||
@@ -551,6 +551,20 @@ _git_describe ()
|
||||
|
||||
_git_diff ()
|
||||
{
|
||||
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
case "$cur" in
|
||||
--*)
|
||||
__gitcomp "--cached --stat --numstat --shortstat --summary
|
||||
--patch-with-stat --name-only --name-status --color
|
||||
--no-color --color-words --no-renames --check
|
||||
--full-index --binary --abbrev --diff-filter
|
||||
--find-copies-harder --pickaxe-all --pickaxe-regex
|
||||
--text --ignore-space-at-eol --ignore-space-change
|
||||
--ignore-all-space --exit-code --quiet --ext-diff
|
||||
--no-ext-diff"
|
||||
return
|
||||
;;
|
||||
esac
|
||||
__git_complete_file
|
||||
}
|
||||
|
||||
|
||||
@@ -12,19 +12,6 @@ die () {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Fix some commands on Windows
|
||||
case $(uname -s) in
|
||||
*MINGW*)
|
||||
# Windows has its own (incompatible) sort and find
|
||||
sort () {
|
||||
/usr/bin/sort "$@"
|
||||
}
|
||||
find () {
|
||||
/usr/bin/find "$@"
|
||||
}
|
||||
;;
|
||||
esac
|
||||
|
||||
exec=
|
||||
while test $# != 0
|
||||
do
|
||||
@@ -9,6 +9,6 @@
|
||||
# because the current index is what we will be committing as the
|
||||
# merge result.
|
||||
|
||||
git diff-index --quiet --cached HEAD || exit 2
|
||||
git diff-index --quiet --cached HEAD -- || exit 2
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -231,7 +231,7 @@ static int handle_diff_files_args(struct rev_info *revs,
|
||||
static int is_outside_repo(const char *path, int nongit, const char *prefix)
|
||||
{
|
||||
int i;
|
||||
if (nongit || !strcmp(path, "-") || path[0] == '/')
|
||||
if (nongit || !strcmp(path, "-") || is_absolute_path(path))
|
||||
return 1;
|
||||
#ifdef __MINGW32__
|
||||
if (!strcmp(path, "nul"))
|
||||
|
||||
@@ -244,28 +244,35 @@ static int find_identical_files(struct file_similarity *src,
|
||||
* Walk over all the destinations ...
|
||||
*/
|
||||
do {
|
||||
struct diff_filespec *one = dst->filespec;
|
||||
struct diff_filespec *target = dst->filespec;
|
||||
struct file_similarity *p, *best;
|
||||
int i = 100;
|
||||
int i = 100, best_score = -1;
|
||||
|
||||
/*
|
||||
* .. to find the best source match
|
||||
*/
|
||||
best = NULL;
|
||||
for (p = src; p; p = p->next) {
|
||||
struct diff_filespec *two = p->filespec;
|
||||
int score;
|
||||
struct diff_filespec *source = p->filespec;
|
||||
|
||||
/* False hash collission? */
|
||||
if (hashcmp(one->sha1, two->sha1))
|
||||
if (hashcmp(source->sha1, target->sha1))
|
||||
continue;
|
||||
/* Non-regular files? If so, the modes must match! */
|
||||
if (!S_ISREG(one->mode) || !S_ISREG(two->mode)) {
|
||||
if (one->mode != two->mode)
|
||||
if (!S_ISREG(source->mode) || !S_ISREG(target->mode)) {
|
||||
if (source->mode != target->mode)
|
||||
continue;
|
||||
}
|
||||
best = p;
|
||||
if (basename_same(one, two))
|
||||
break;
|
||||
/* Give higher scores to sources that haven't been used already */
|
||||
score = !source->rename_used;
|
||||
score += basename_same(source, target);
|
||||
if (score > best_score) {
|
||||
best = p;
|
||||
best_score = score;
|
||||
if (score == 2)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Too many identical alternatives? Pick one */
|
||||
if (!--i)
|
||||
@@ -488,6 +495,19 @@ void diffcore_rename(struct diff_options *options)
|
||||
}
|
||||
/* cost matrix sorted by most to least similar pair */
|
||||
qsort(mx, num_create * num_src, sizeof(*mx), score_compare);
|
||||
for (i = 0; i < num_create * num_src; i++) {
|
||||
struct diff_rename_dst *dst = &rename_dst[mx[i].dst];
|
||||
struct diff_filespec *src;
|
||||
if (dst->pair)
|
||||
continue; /* already done, either exact or fuzzy. */
|
||||
if (mx[i].score < minimum_score)
|
||||
break; /* there is no more usable pair. */
|
||||
src = rename_src[mx[i].src].one;
|
||||
if (src->rename_used)
|
||||
continue;
|
||||
record_rename_pair(mx[i].dst, mx[i].src, mx[i].score);
|
||||
rename_count++;
|
||||
}
|
||||
for (i = 0; i < num_create * num_src; i++) {
|
||||
struct diff_rename_dst *dst = &rename_dst[mx[i].dst];
|
||||
if (dst->pair)
|
||||
|
||||
114
dir.c
114
dir.c
@@ -144,17 +144,14 @@ void add_exclude(const char *string, const char *base,
|
||||
x->flags |= EXC_FLAG_NOWILDCARD;
|
||||
if (*string == '*' && no_wildcard(string+1))
|
||||
x->flags |= EXC_FLAG_ENDSWITH;
|
||||
if (which->nr == which->alloc) {
|
||||
which->alloc = alloc_nr(which->alloc);
|
||||
which->excludes = xrealloc(which->excludes,
|
||||
which->alloc * sizeof(x));
|
||||
}
|
||||
ALLOC_GROW(which->excludes, which->nr + 1, which->alloc);
|
||||
which->excludes[which->nr++] = x;
|
||||
}
|
||||
|
||||
static int add_excludes_from_file_1(const char *fname,
|
||||
const char *base,
|
||||
int baselen,
|
||||
char **buf_p,
|
||||
struct exclude_list *which)
|
||||
{
|
||||
struct stat st;
|
||||
@@ -175,6 +172,8 @@ static int add_excludes_from_file_1(const char *fname,
|
||||
goto err;
|
||||
close(fd);
|
||||
|
||||
if (buf_p)
|
||||
*buf_p = buf;
|
||||
buf[size++] = '\n';
|
||||
entry = buf;
|
||||
for (i = 0; i < size; i++) {
|
||||
@@ -196,31 +195,63 @@ static int add_excludes_from_file_1(const char *fname,
|
||||
|
||||
void add_excludes_from_file(struct dir_struct *dir, const char *fname)
|
||||
{
|
||||
if (add_excludes_from_file_1(fname, "", 0,
|
||||
if (add_excludes_from_file_1(fname, "", 0, NULL,
|
||||
&dir->exclude_list[EXC_FILE]) < 0)
|
||||
die("cannot use %s as an exclude file", fname);
|
||||
}
|
||||
|
||||
int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen)
|
||||
static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
|
||||
{
|
||||
char exclude_file[PATH_MAX];
|
||||
struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
|
||||
int current_nr = el->nr;
|
||||
struct exclude_list *el;
|
||||
struct exclude_stack *stk = NULL;
|
||||
int current;
|
||||
|
||||
if (dir->exclude_per_dir) {
|
||||
memcpy(exclude_file, base, baselen);
|
||||
strcpy(exclude_file + baselen, dir->exclude_per_dir);
|
||||
add_excludes_from_file_1(exclude_file, base, baselen, el);
|
||||
if ((!dir->exclude_per_dir) ||
|
||||
(baselen + strlen(dir->exclude_per_dir) >= PATH_MAX))
|
||||
return; /* too long a path -- ignore */
|
||||
|
||||
/* Pop the ones that are not the prefix of the path being checked. */
|
||||
el = &dir->exclude_list[EXC_DIRS];
|
||||
while ((stk = dir->exclude_stack) != NULL) {
|
||||
if (stk->baselen <= baselen &&
|
||||
!strncmp(dir->basebuf, base, stk->baselen))
|
||||
break;
|
||||
dir->exclude_stack = stk->prev;
|
||||
while (stk->exclude_ix < el->nr)
|
||||
free(el->excludes[--el->nr]);
|
||||
free(stk->filebuf);
|
||||
free(stk);
|
||||
}
|
||||
return current_nr;
|
||||
}
|
||||
|
||||
void pop_exclude_per_directory(struct dir_struct *dir, int stk)
|
||||
{
|
||||
struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
|
||||
/* Read from the parent directories and push them down. */
|
||||
current = stk ? stk->baselen : -1;
|
||||
while (current < baselen) {
|
||||
struct exclude_stack *stk = xcalloc(1, sizeof(*stk));
|
||||
const char *cp;
|
||||
|
||||
while (stk < el->nr)
|
||||
free(el->excludes[--el->nr]);
|
||||
if (current < 0) {
|
||||
cp = base;
|
||||
current = 0;
|
||||
}
|
||||
else {
|
||||
cp = strchr(base + current + 1, '/');
|
||||
if (!cp)
|
||||
die("oops in prep_exclude");
|
||||
cp++;
|
||||
}
|
||||
stk->prev = dir->exclude_stack;
|
||||
stk->baselen = cp - base;
|
||||
stk->exclude_ix = el->nr;
|
||||
memcpy(dir->basebuf + current, base + current,
|
||||
stk->baselen - current);
|
||||
strcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);
|
||||
add_excludes_from_file_1(dir->basebuf,
|
||||
dir->basebuf, stk->baselen,
|
||||
&stk->filebuf, el);
|
||||
dir->exclude_stack = stk;
|
||||
current = stk->baselen;
|
||||
}
|
||||
dir->basebuf[baselen] = '\0';
|
||||
}
|
||||
|
||||
/* Scan the list and let the last match determines the fate.
|
||||
@@ -287,6 +318,7 @@ int excluded(struct dir_struct *dir, const char *pathname)
|
||||
const char *basename = strrchr(pathname, '/');
|
||||
basename = (basename) ? basename+1 : pathname;
|
||||
|
||||
prep_exclude(dir, pathname, basename-pathname);
|
||||
for (st = EXC_CMDL; st <= EXC_FILE; st++) {
|
||||
switch (excluded_1(pathname, pathlen, basename, &dir->exclude_list[st])) {
|
||||
case 0:
|
||||
@@ -504,13 +536,10 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
|
||||
int contents = 0;
|
||||
|
||||
if (fdir) {
|
||||
int exclude_stk;
|
||||
struct dirent *de;
|
||||
char fullname[PATH_MAX + 1];
|
||||
memcpy(fullname, base, baselen);
|
||||
|
||||
exclude_stk = push_exclude_per_directory(dir, base, baselen);
|
||||
|
||||
while ((de = readdir(fdir)) != NULL) {
|
||||
int len, dtype;
|
||||
int exclude;
|
||||
@@ -584,8 +613,6 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
|
||||
}
|
||||
exit_early:
|
||||
closedir(fdir);
|
||||
|
||||
pop_exclude_per_directory(dir, exclude_stk);
|
||||
}
|
||||
|
||||
return contents;
|
||||
@@ -654,47 +681,18 @@ static void free_simplify(struct path_simplify *simplify)
|
||||
int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
|
||||
{
|
||||
struct path_simplify *simplify = create_simplify(pathspec);
|
||||
char *pp = NULL;
|
||||
|
||||
/*
|
||||
* Make sure to do the per-directory exclude for all the
|
||||
* directories leading up to our base.
|
||||
*/
|
||||
if (baselen) {
|
||||
if (dir->exclude_per_dir) {
|
||||
char *p;
|
||||
pp = xmalloc(baselen+1);
|
||||
memcpy(pp, base, baselen+1);
|
||||
p = pp;
|
||||
while (1) {
|
||||
char save = *p;
|
||||
*p = 0;
|
||||
push_exclude_per_directory(dir, pp, p-pp);
|
||||
*p++ = save;
|
||||
if (!save)
|
||||
break;
|
||||
p = strchr(p, '/');
|
||||
if (p)
|
||||
p++;
|
||||
else
|
||||
p = pp + baselen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
read_directory_recursive(dir, path, base, baselen, 0, simplify);
|
||||
free_simplify(simplify);
|
||||
free(pp);
|
||||
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
|
||||
qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
|
||||
return dir->nr;
|
||||
}
|
||||
|
||||
int
|
||||
file_exists(const char *f)
|
||||
int file_exists(const char *f)
|
||||
{
|
||||
struct stat sb;
|
||||
return stat(f, &sb) == 0;
|
||||
struct stat sb;
|
||||
return stat(f, &sb) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
32
dir.h
32
dir.h
@@ -1,17 +1,6 @@
|
||||
#ifndef DIR_H
|
||||
#define DIR_H
|
||||
|
||||
/*
|
||||
* We maintain three exclude pattern lists:
|
||||
* EXC_CMDL lists patterns explicitly given on the command line.
|
||||
* EXC_DIRS lists patterns obtained from per-directory ignore files.
|
||||
* EXC_FILE lists patterns from fallback ignore files.
|
||||
*/
|
||||
#define EXC_CMDL 0
|
||||
#define EXC_DIRS 1
|
||||
#define EXC_FILE 2
|
||||
|
||||
|
||||
struct dir_entry {
|
||||
unsigned int len;
|
||||
char name[FLEX_ARRAY]; /* more */
|
||||
@@ -34,6 +23,13 @@ struct exclude_list {
|
||||
} **excludes;
|
||||
};
|
||||
|
||||
struct exclude_stack {
|
||||
struct exclude_stack *prev;
|
||||
char *filebuf;
|
||||
int baselen;
|
||||
int exclude_ix;
|
||||
};
|
||||
|
||||
struct dir_struct {
|
||||
int nr, alloc;
|
||||
int ignored_nr, ignored_alloc;
|
||||
@@ -48,6 +44,18 @@ struct dir_struct {
|
||||
/* Exclude info */
|
||||
const char *exclude_per_dir;
|
||||
struct exclude_list exclude_list[3];
|
||||
/*
|
||||
* We maintain three exclude pattern lists:
|
||||
* EXC_CMDL lists patterns explicitly given on the command line.
|
||||
* EXC_DIRS lists patterns obtained from per-directory ignore files.
|
||||
* EXC_FILE lists patterns from fallback ignore files.
|
||||
*/
|
||||
#define EXC_CMDL 0
|
||||
#define EXC_DIRS 1
|
||||
#define EXC_FILE 2
|
||||
|
||||
struct exclude_stack *exclude_stack;
|
||||
char basebuf[PATH_MAX];
|
||||
};
|
||||
|
||||
extern int common_prefix(const char **pathspec);
|
||||
@@ -58,8 +66,6 @@ extern int common_prefix(const char **pathspec);
|
||||
extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
|
||||
|
||||
extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen, const char **pathspec);
|
||||
extern int push_exclude_per_directory(struct dir_struct *, const char *, int);
|
||||
extern void pop_exclude_per_directory(struct dir_struct *, int);
|
||||
|
||||
extern int excluded(struct dir_struct *, const char *);
|
||||
extern void add_excludes_from_file(struct dir_struct *, const char *fname);
|
||||
|
||||
@@ -218,7 +218,7 @@ fi
|
||||
|
||||
case "$resolved" in
|
||||
'')
|
||||
files=$(git diff-index --cached --name-only HEAD) || exit
|
||||
files=$(git diff-index --cached --name-only HEAD --) || exit
|
||||
if [ "$files" ]; then
|
||||
echo "Dirty index: cannot apply patches (dirty: $files)" >&2
|
||||
exit 1
|
||||
@@ -352,7 +352,7 @@ do
|
||||
case "$resolved$interactive" in
|
||||
tt)
|
||||
# This is used only for interactive view option.
|
||||
git diff-index -p --cached HEAD >"$dotest/patch"
|
||||
git diff-index -p --cached HEAD -- >"$dotest/patch"
|
||||
;;
|
||||
esac
|
||||
esac
|
||||
@@ -411,7 +411,7 @@ do
|
||||
# trust what the user has in the index file and the
|
||||
# working tree.
|
||||
resolved=
|
||||
git diff-index --quiet --cached HEAD && {
|
||||
git diff-index --quiet --cached HEAD -- && {
|
||||
echo "No changes - did you forget to use 'git add'?"
|
||||
stop_here_user_resolve $this
|
||||
}
|
||||
@@ -433,7 +433,7 @@ do
|
||||
then
|
||||
# Applying the patch to an earlier tree and merging the
|
||||
# result may have produced the same tree as ours.
|
||||
git diff-index --quiet --cached HEAD && {
|
||||
git diff-index --quiet --cached HEAD -- && {
|
||||
echo No changes -- Patch already applied.
|
||||
go_next
|
||||
continue
|
||||
|
||||
@@ -37,7 +37,7 @@ sq() {
|
||||
}
|
||||
|
||||
bisect_autostart() {
|
||||
test -d "$GIT_DIR/refs/bisect" || {
|
||||
test -f "$GIT_DIR/BISECT_NAMES" || {
|
||||
echo >&2 'You need to start by "git bisect start"'
|
||||
if test -t 0
|
||||
then
|
||||
@@ -72,7 +72,7 @@ bisect_start() {
|
||||
;;
|
||||
refs/heads/*)
|
||||
[ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
|
||||
echo "$head" | sed 's#^refs/heads/##' >"$GIT_DIR/head-name"
|
||||
echo "${head#refs/heads/}" >"$GIT_DIR/head-name"
|
||||
;;
|
||||
*)
|
||||
die "Bad HEAD - strange symbolic ref"
|
||||
@@ -83,7 +83,6 @@ bisect_start() {
|
||||
# Get rid of any old bisect state
|
||||
#
|
||||
bisect_clean_state
|
||||
mkdir "$GIT_DIR/refs/bisect"
|
||||
|
||||
#
|
||||
# Check for one bad and then some good revisions.
|
||||
@@ -131,7 +130,7 @@ bisect_write() {
|
||||
good|skip) tag="$state"-"$rev" ;;
|
||||
*) die "Bad bisect_write argument: $state" ;;
|
||||
esac
|
||||
echo "$rev" >"$GIT_DIR/refs/bisect/$tag"
|
||||
git update-ref "refs/bisect/$tag" "$rev"
|
||||
echo "# $state: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
|
||||
test -z "$nolog" && echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
|
||||
}
|
||||
@@ -192,7 +191,7 @@ bisect_next_check() {
|
||||
;;
|
||||
*)
|
||||
THEN=''
|
||||
test -d "$GIT_DIR/refs/bisect" || {
|
||||
test -f "$GIT_DIR/BISECT_NAMES" || {
|
||||
echo >&2 'You need to start by "git bisect start".'
|
||||
THEN='then '
|
||||
}
|
||||
@@ -276,8 +275,7 @@ exit_if_skipped_commits () {
|
||||
if expr "$_tried" : ".*[|].*" > /dev/null ; then
|
||||
echo "There are only 'skip'ped commit left to test."
|
||||
echo "The first bad commit could be any of:"
|
||||
echo "$_tried" | sed -e 's/[|]/\
|
||||
/g'
|
||||
echo "$_tried" | tr '[|]' '[\012]'
|
||||
echo "We cannot bisect more!"
|
||||
exit 2
|
||||
fi
|
||||
@@ -318,20 +316,23 @@ bisect_next() {
|
||||
exit_if_skipped_commits "$bisect_rev"
|
||||
|
||||
echo "Bisecting: $bisect_nr revisions left to test after this"
|
||||
echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect"
|
||||
git branch -f new-bisect "$bisect_rev"
|
||||
git checkout -q new-bisect || exit
|
||||
mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
|
||||
GIT_DIR="$GIT_DIR" git symbolic-ref HEAD refs/heads/bisect
|
||||
git branch -M new-bisect bisect
|
||||
git show-branch "$bisect_rev"
|
||||
}
|
||||
|
||||
bisect_visualize() {
|
||||
bisect_next_check fail
|
||||
not=`cd "$GIT_DIR/refs" && echo bisect/good-*`
|
||||
eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
|
||||
not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*")
|
||||
eval gitk refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
|
||||
}
|
||||
|
||||
bisect_reset() {
|
||||
test -f "$GIT_DIR/BISECT_NAMES" || {
|
||||
echo "We are not bisecting."
|
||||
return
|
||||
}
|
||||
case "$#" in
|
||||
0) if [ -s "$GIT_DIR/head-name" ]; then
|
||||
branch=`cat "$GIT_DIR/head-name"`
|
||||
@@ -351,8 +352,12 @@ bisect_reset() {
|
||||
}
|
||||
|
||||
bisect_clean_state() {
|
||||
rm -fr "$GIT_DIR/refs/bisect"
|
||||
rm -f "$GIT_DIR/refs/heads/bisect"
|
||||
# There may be some refs packed during bisection.
|
||||
git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* refs/heads/bisect |
|
||||
while read ref hash
|
||||
do
|
||||
git update-ref -d $ref $hash
|
||||
done
|
||||
rm -f "$GIT_DIR/BISECT_LOG"
|
||||
rm -f "$GIT_DIR/BISECT_NAMES"
|
||||
rm -f "$GIT_DIR/BISECT_RUN"
|
||||
|
||||
@@ -175,7 +175,7 @@ detach_warn=
|
||||
describe_detached_head () {
|
||||
test -n "$quiet" || {
|
||||
printf >&2 "$1 "
|
||||
GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2"
|
||||
GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2" --
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ if [ "$?" -eq 0 ]; then
|
||||
if test -n "$branch"
|
||||
then
|
||||
old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'`
|
||||
GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from $old_branch_name to $branch" HEAD "refs/heads/$branch"
|
||||
GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from ${old_branch_name:-$old} to $branch" HEAD "refs/heads/$branch"
|
||||
if test -n "$quiet"
|
||||
then
|
||||
true # nothing
|
||||
@@ -278,7 +278,8 @@ if [ "$?" -eq 0 ]; then
|
||||
fi
|
||||
elif test -n "$detached"
|
||||
then
|
||||
git update-ref --no-deref -m "checkout: moving to $arg" HEAD "$detached" ||
|
||||
old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'`
|
||||
git update-ref --no-deref -m "checkout: moving from ${old_branch_name:-$old} to $arg" HEAD "$detached" ||
|
||||
die "Cannot detach HEAD"
|
||||
if test -n "$detach_warn"
|
||||
then
|
||||
|
||||
@@ -108,10 +108,6 @@ sub read_repo_config {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (@ARGV == 0) {
|
||||
chomp(my $module = `git-repo-config --get cvsimport.module`);
|
||||
push(@ARGV, $module);
|
||||
}
|
||||
}
|
||||
|
||||
my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:";
|
||||
@@ -119,6 +115,10 @@ read_repo_config($opts);
|
||||
getopts($opts) or usage();
|
||||
usage if $opt_h;
|
||||
|
||||
if (@ARGV == 0) {
|
||||
chomp(my $module = `git-repo-config --get cvsimport.module`);
|
||||
push(@ARGV, $module) if $? == 0;
|
||||
}
|
||||
@ARGV <= 1 or usage("You can't specify more than one CVS module");
|
||||
|
||||
if ($opt_d) {
|
||||
@@ -527,18 +527,12 @@ sub is_sha1 {
|
||||
return $s =~ /^[a-f0-9]{40}$/;
|
||||
}
|
||||
|
||||
sub get_headref ($$) {
|
||||
my $name = shift;
|
||||
my $git_dir = shift;
|
||||
|
||||
my $f = "$git_dir/$remote/$name";
|
||||
if (open(my $fh, $f)) {
|
||||
chomp(my $r = <$fh>);
|
||||
is_sha1($r) or die "Cannot get head id for $name ($r): $!";
|
||||
return $r;
|
||||
}
|
||||
die "unable to open $f: $!" unless $! == POSIX::ENOENT;
|
||||
return undef;
|
||||
sub get_headref ($) {
|
||||
my $name = shift;
|
||||
my $r = `git rev-parse --verify '$name' 2>/dev/null`;
|
||||
return undef unless $? == 0;
|
||||
chomp $r;
|
||||
return $r;
|
||||
}
|
||||
|
||||
-d $git_tree
|
||||
@@ -698,7 +692,8 @@ my (@old,@new,@skipped,%ignorebranch);
|
||||
$ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
|
||||
|
||||
sub commit {
|
||||
if ($branch eq $opt_o && !$index{branch} && !get_headref($branch, $git_dir)) {
|
||||
if ($branch eq $opt_o && !$index{branch} &&
|
||||
!get_headref("$remote/$branch")) {
|
||||
# looks like an initial commit
|
||||
# use the index primed by git-init
|
||||
$ENV{GIT_INDEX_FILE} = "$git_dir/index";
|
||||
@@ -722,7 +717,7 @@ sub commit {
|
||||
update_index(@old, @new);
|
||||
@old = @new = ();
|
||||
my $tree = write_tree();
|
||||
my $parent = get_headref($last_branch, $git_dir);
|
||||
my $parent = get_headref("$remote/$last_branch");
|
||||
print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v;
|
||||
|
||||
my @commit_args;
|
||||
@@ -733,7 +728,7 @@ sub commit {
|
||||
foreach my $rx (@mergerx) {
|
||||
next unless $logmsg =~ $rx && $1;
|
||||
my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
|
||||
if (my $sha1 = get_headref($mparent, $git_dir)) {
|
||||
if (my $sha1 = get_headref("$remote/$mparent")) {
|
||||
push @commit_args, '-p', $mparent;
|
||||
print "Merge parent branch: $mparent\n" if $opt_v;
|
||||
}
|
||||
@@ -870,29 +865,27 @@ while (<CVS>) {
|
||||
print STDERR "Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\n";
|
||||
$ancestor = $opt_o;
|
||||
}
|
||||
if (-f "$git_dir/$remote/$branch") {
|
||||
if (defined get_headref("$remote/$branch")) {
|
||||
print STDERR "Branch $branch already exists!\n";
|
||||
$state=11;
|
||||
next;
|
||||
}
|
||||
unless (open(H,"$git_dir/$remote/$ancestor")) {
|
||||
my $id = get_headref("$remote/$ancestor");
|
||||
if (!$id) {
|
||||
print STDERR "Branch $ancestor does not exist!\n";
|
||||
$ignorebranch{$branch} = 1;
|
||||
$state=11;
|
||||
next;
|
||||
}
|
||||
chomp(my $id = <H>);
|
||||
close(H);
|
||||
unless (open(H,"> $git_dir/$remote/$branch")) {
|
||||
print STDERR "Could not create branch $branch: $!\n";
|
||||
|
||||
system(qw(git update-ref -m cvsimport),
|
||||
"$remote/$branch", $id);
|
||||
if($? != 0) {
|
||||
print STDERR "Could not create branch $branch\n";
|
||||
$ignorebranch{$branch} = 1;
|
||||
$state=11;
|
||||
next;
|
||||
}
|
||||
print H "$id\n"
|
||||
or die "Could not write branch $branch: $!";
|
||||
close(H)
|
||||
or die "Could not write branch $branch: $!";
|
||||
}
|
||||
$last_branch = $branch if $branch ne $last_branch;
|
||||
$state = 9;
|
||||
@@ -1004,7 +997,7 @@ if ($orig_branch) {
|
||||
$orig_branch = "master";
|
||||
print "DONE; creating $orig_branch branch\n" if $opt_v;
|
||||
system("git-update-ref", "refs/heads/master", "$remote/$opt_o")
|
||||
unless -f "$git_dir/refs/heads/master";
|
||||
unless defined get_headref('refs/heads/master');
|
||||
system("git-symbolic-ref", "$remote/HEAD", "$remote/$opt_o")
|
||||
if ($opt_r && $opt_o ne 'HEAD');
|
||||
system('git-update-ref', 'HEAD', "$orig_branch");
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
# a new branch. You can specify a number of filters to modify the commits,
|
||||
# files and trees.
|
||||
|
||||
# The following functions will also be available in the commit filter:
|
||||
|
||||
functions=$(cat << \EOF
|
||||
warn () {
|
||||
echo "$*" >&2
|
||||
}
|
||||
@@ -46,6 +49,10 @@ die()
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
eval "$functions"
|
||||
|
||||
# When piped a commit, output a script to set the ident of either
|
||||
# "author" or "committer
|
||||
@@ -80,11 +87,6 @@ set_ident () {
|
||||
echo "[ -n \"\$GIT_${uid}_NAME\" ] || export GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\""
|
||||
}
|
||||
|
||||
# This script can be sourced by the commit filter to get the functions
|
||||
test "a$SOURCE_FUNCTIONS" = a1 && return
|
||||
this_script="$(cd "$(dirname "$0")"; pwd)"/$(basename "$0")
|
||||
export this_script
|
||||
|
||||
USAGE="[--env-filter <command>] [--tree-filter <command>] \
|
||||
[--index-filter <command>] [--parent-filter <command>] \
|
||||
[--msg-filter <command>] [--commit-filter <command>] \
|
||||
@@ -96,7 +98,7 @@ OPTIONS_SPEC=
|
||||
. git-sh-setup
|
||||
|
||||
git diff-files --quiet &&
|
||||
git diff-index --cached --quiet HEAD ||
|
||||
git diff-index --cached --quiet HEAD -- ||
|
||||
die "Cannot rewrite branch(es) with a dirty working directory."
|
||||
|
||||
tempdir=.git-rewrite
|
||||
@@ -156,7 +158,7 @@ do
|
||||
filter_msg="$OPTARG"
|
||||
;;
|
||||
--commit-filter)
|
||||
filter_commit='SOURCE_FUNCTIONS=1 . "$this_script";'" $OPTARG"
|
||||
filter_commit="$functions; $OPTARG"
|
||||
;;
|
||||
--tag-name-filter)
|
||||
filter_tag_name="$OPTARG"
|
||||
|
||||
@@ -53,7 +53,7 @@ require_clean_work_tree () {
|
||||
git rev-parse --verify HEAD > /dev/null &&
|
||||
git update-index --refresh &&
|
||||
git diff-files --quiet &&
|
||||
git diff-index --cached --quiet HEAD ||
|
||||
git diff-index --cached --quiet HEAD -- ||
|
||||
die "Working tree is dirty"
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@ do
|
||||
git rev-parse --verify HEAD > /dev/null &&
|
||||
git update-index --refresh &&
|
||||
git diff-files --quiet &&
|
||||
! git diff-index --cached --quiet HEAD &&
|
||||
! git diff-index --cached --quiet HEAD -- &&
|
||||
. "$DOTEST"/author-script && {
|
||||
test ! -f "$DOTEST"/amend || git reset --soft HEAD^
|
||||
} &&
|
||||
|
||||
@@ -61,7 +61,7 @@ continue_merge () {
|
||||
fi
|
||||
|
||||
cmt=`cat "$dotest/current"`
|
||||
if ! git diff-index --quiet HEAD
|
||||
if ! git diff-index --quiet HEAD --
|
||||
then
|
||||
if ! git-commit -C "$cmt"
|
||||
then
|
||||
@@ -179,6 +179,7 @@ do
|
||||
exit
|
||||
;;
|
||||
--skip)
|
||||
git reset --hard HEAD || exit $?
|
||||
if test -d "$dotest"
|
||||
then
|
||||
git rerere clear
|
||||
@@ -284,7 +285,7 @@ fi
|
||||
|
||||
# The tree must be really really clean.
|
||||
git update-index --refresh || exit
|
||||
diff=$(git diff-index --cached --name-status -r HEAD)
|
||||
diff=$(git diff-index --cached --name-status -r HEAD --)
|
||||
case "$diff" in
|
||||
?*) echo "cannot rebase: your index is not up-to-date"
|
||||
echo "$diff"
|
||||
|
||||
@@ -15,7 +15,7 @@ trap 'rm -f "$TMP-*"' 0
|
||||
ref_stash=refs/stash
|
||||
|
||||
no_changes () {
|
||||
git diff-index --quiet --cached HEAD &&
|
||||
git diff-index --quiet --cached HEAD -- &&
|
||||
git diff-files --quiet
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ create_stash () {
|
||||
# state of the base commit
|
||||
if b_commit=$(git rev-parse --verify HEAD)
|
||||
then
|
||||
head=$(git log --abbrev-commit --pretty=oneline -n 1 HEAD)
|
||||
head=$(git log --no-color --abbrev-commit --pretty=oneline -n 1 HEAD --)
|
||||
else
|
||||
die "You do not have the initial commit yet"
|
||||
fi
|
||||
@@ -108,7 +108,7 @@ have_stash () {
|
||||
|
||||
list_stash () {
|
||||
have_stash || return 0
|
||||
git log --pretty=oneline -g "$@" $ref_stash |
|
||||
git log --no-color --pretty=oneline -g "$@" $ref_stash -- |
|
||||
sed -n -e 's/^[.0-9a-f]* refs\///p'
|
||||
}
|
||||
|
||||
|
||||
4
git.c
4
git.c
@@ -293,6 +293,7 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
{ "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "cherry", cmd_cherry, RUN_SETUP },
|
||||
{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "commit-tree", cmd_commit_tree, RUN_SETUP },
|
||||
{ "config", cmd_config },
|
||||
{ "count-objects", cmd_count_objects, RUN_SETUP },
|
||||
@@ -321,6 +322,7 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
{ "log", cmd_log, RUN_SETUP | USE_PAGER },
|
||||
{ "ls-files", cmd_ls_files, RUN_SETUP },
|
||||
{ "ls-tree", cmd_ls_tree, RUN_SETUP },
|
||||
{ "ls-remote", cmd_ls_remote },
|
||||
{ "mailinfo", cmd_mailinfo },
|
||||
{ "mailsplit", cmd_mailsplit },
|
||||
{ "merge-base", cmd_merge_base, RUN_SETUP },
|
||||
@@ -329,6 +331,7 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "name-rev", cmd_name_rev, RUN_SETUP },
|
||||
{ "pack-objects", cmd_pack_objects, RUN_SETUP },
|
||||
{ "peek-remote", cmd_ls_remote },
|
||||
{ "pickaxe", cmd_blame, RUN_SETUP },
|
||||
{ "prune", cmd_prune, RUN_SETUP },
|
||||
{ "prune-packed", cmd_prune_packed, RUN_SETUP },
|
||||
@@ -343,6 +346,7 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "rm", cmd_rm, RUN_SETUP },
|
||||
{ "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "send-pack", cmd_send_pack, RUN_SETUP },
|
||||
{ "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
|
||||
{ "show-branch", cmd_show_branch, RUN_SETUP },
|
||||
{ "show", cmd_show, RUN_SETUP | USE_PAGER },
|
||||
|
||||
10
http-push.c
10
http-push.c
@@ -78,7 +78,7 @@ static struct curl_slist *no_pragma_header;
|
||||
static struct curl_slist *default_headers;
|
||||
|
||||
static int push_verbosely;
|
||||
static int push_all;
|
||||
static int push_all = MATCH_REFS_NONE;
|
||||
static int force_all;
|
||||
static int dry_run;
|
||||
|
||||
@@ -433,7 +433,7 @@ static void start_fetch_packed(struct transfer_request *request)
|
||||
packfile = fopen(request->tmpfile, "a");
|
||||
if (!packfile) {
|
||||
fprintf(stderr, "Unable to open local file %s for pack",
|
||||
filename);
|
||||
request->tmpfile);
|
||||
remote->can_update_info_refs = 0;
|
||||
free(url);
|
||||
return;
|
||||
@@ -941,7 +941,7 @@ static int fetch_index(unsigned char *sha1)
|
||||
indexfile = fopen(tmpfile, "a");
|
||||
if (!indexfile)
|
||||
return error("Unable to open local file %s for pack index",
|
||||
filename);
|
||||
tmpfile);
|
||||
|
||||
slot = get_active_slot();
|
||||
slot->results = &results;
|
||||
@@ -2300,7 +2300,7 @@ int main(int argc, char **argv)
|
||||
|
||||
if (*arg == '-') {
|
||||
if (!strcmp(arg, "--all")) {
|
||||
push_all = 1;
|
||||
push_all = MATCH_REFS_ALL;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--force")) {
|
||||
@@ -2393,7 +2393,7 @@ int main(int argc, char **argv)
|
||||
if (!remote_tail)
|
||||
remote_tail = &remote_refs;
|
||||
if (match_refs(local_refs, remote_refs, &remote_tail,
|
||||
nr_refspec, refspec, push_all))
|
||||
nr_refspec, (const char **) refspec, push_all))
|
||||
return -1;
|
||||
if (!remote_refs) {
|
||||
fprintf(stderr, "No refs in common and none specified; doing nothing.\n");
|
||||
|
||||
@@ -405,7 +405,7 @@ static int fetch_index(struct walker *walker, struct alt_base *repo, unsigned ch
|
||||
indexfile = fopen(tmpfile, "a");
|
||||
if (!indexfile)
|
||||
return error("Unable to open local file %s for pack index",
|
||||
filename);
|
||||
tmpfile);
|
||||
|
||||
slot = get_active_slot();
|
||||
slot->results = &results;
|
||||
@@ -770,7 +770,7 @@ static int fetch_pack(struct walker *walker, struct alt_base *repo, unsigned cha
|
||||
packfile = fopen(tmpfile, "a");
|
||||
if (!packfile)
|
||||
return error("Unable to open local file %s for pack",
|
||||
filename);
|
||||
tmpfile);
|
||||
|
||||
slot = get_active_slot();
|
||||
slot->results = &results;
|
||||
|
||||
@@ -94,7 +94,7 @@ static char *resolve_symlink(char *p, size_t s)
|
||||
return p;
|
||||
}
|
||||
|
||||
if (link[0] == '/') {
|
||||
if (is_absolute_path(link)) {
|
||||
/* absolute path simply replaces p */
|
||||
if (link_len < s)
|
||||
strcpy(p, link);
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
#include "cache.h"
|
||||
#include "refs.h"
|
||||
#include "pkt-line.h"
|
||||
|
||||
static const char peek_remote_usage[] =
|
||||
"git-peek-remote [--upload-pack=<git-upload-pack>] [<host>:]<directory>";
|
||||
static const char *uploadpack = "git-upload-pack";
|
||||
|
||||
static int peek_remote(int fd[2], unsigned flags)
|
||||
{
|
||||
struct ref *ref;
|
||||
|
||||
get_remote_heads(fd[0], &ref, 0, NULL, flags);
|
||||
packet_flush(fd[1]);
|
||||
|
||||
while (ref) {
|
||||
printf("%s %s\n", sha1_to_hex(ref->old_sha1), ref->name);
|
||||
ref = ref->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i, ret;
|
||||
char *dest = NULL;
|
||||
int fd[2];
|
||||
struct child_process *conn;
|
||||
int nongit = 0;
|
||||
unsigned flags = 0;
|
||||
|
||||
setup_git_directory_gently(&nongit);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
char *arg = argv[i];
|
||||
|
||||
if (*arg == '-') {
|
||||
if (!prefixcmp(arg, "--upload-pack=")) {
|
||||
uploadpack = arg + 14;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--exec=")) {
|
||||
uploadpack = arg + 7;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--tags", arg)) {
|
||||
flags |= REF_TAGS;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--heads", arg)) {
|
||||
flags |= REF_HEADS;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--refs", arg)) {
|
||||
flags |= REF_NORMAL;
|
||||
continue;
|
||||
}
|
||||
usage(peek_remote_usage);
|
||||
}
|
||||
dest = arg;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!dest || i != argc - 1)
|
||||
usage(peek_remote_usage);
|
||||
|
||||
conn = git_connect(fd, dest, uploadpack, 0);
|
||||
ret = peek_remote(fd, flags);
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
ret |= finish_connect(conn);
|
||||
return !!ret;
|
||||
}
|
||||
31
perl/Git.pm
31
perl/Git.pm
@@ -549,6 +549,37 @@ sub config_bool {
|
||||
};
|
||||
}
|
||||
|
||||
=item config_int ( VARIABLE )
|
||||
|
||||
Retrieve the integer configuration C<VARIABLE>. The return value
|
||||
is simple decimal number. An optional value suffix of 'k', 'm',
|
||||
or 'g' in the config file will cause the value to be multiplied
|
||||
by 1024, 1048576 (1024^2), or 1073741824 (1024^3) prior to output.
|
||||
It would return C<undef> if configuration variable is not defined,
|
||||
|
||||
Must be called on a repository instance.
|
||||
|
||||
This currently wraps command('config') so it is not so fast.
|
||||
|
||||
=cut
|
||||
|
||||
sub config_int {
|
||||
my ($self, $var) = @_;
|
||||
$self->repo_path()
|
||||
or throw Error::Simple("not a repository");
|
||||
|
||||
try {
|
||||
return $self->command_oneline('config', '--int', '--get', $var);
|
||||
} catch Git::Error::Command with {
|
||||
my $E = shift;
|
||||
if ($E->value() == 1) {
|
||||
# Key not found.
|
||||
return undef;
|
||||
} else {
|
||||
throw $E;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
=item ident ( TYPE | IDENTSTR )
|
||||
|
||||
|
||||
@@ -200,12 +200,14 @@ static const char *update(struct command *cmd)
|
||||
}
|
||||
|
||||
if (is_null_sha1(new_sha1)) {
|
||||
if (!parse_object(old_sha1)) {
|
||||
warning ("Allowing deletion of corrupt ref.");
|
||||
old_sha1 = NULL;
|
||||
}
|
||||
if (delete_ref(name, old_sha1)) {
|
||||
error("failed to delete %s", name);
|
||||
return "failed to delete";
|
||||
}
|
||||
fprintf(stderr, "%s: %s -> deleted\n", name,
|
||||
sha1_to_hex(old_sha1));
|
||||
return NULL; /* good */
|
||||
}
|
||||
else {
|
||||
@@ -217,8 +219,6 @@ static const char *update(struct command *cmd)
|
||||
if (write_ref_sha1(lock, new_sha1, "push")) {
|
||||
return "failed to write"; /* error() already called */
|
||||
}
|
||||
fprintf(stderr, "%s: %s -> %s\n", name,
|
||||
sha1_to_hex(old_sha1), sha1_to_hex(new_sha1));
|
||||
return NULL; /* good */
|
||||
}
|
||||
}
|
||||
|
||||
8
refs.c
8
refs.c
@@ -1433,3 +1433,11 @@ int update_ref(const char *action, const char *refname,
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ref *find_ref_by_name(struct ref *list, const char *name)
|
||||
{
|
||||
for ( ; list; list = list->next)
|
||||
if (!strcmp(list->name, name))
|
||||
return list;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
53
remote.c
53
remote.c
@@ -485,7 +485,7 @@ struct ref *alloc_ref(unsigned namelen)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct ref *copy_ref(struct ref *ref)
|
||||
static struct ref *copy_ref(const struct ref *ref)
|
||||
{
|
||||
struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1);
|
||||
memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1);
|
||||
@@ -493,6 +493,18 @@ static struct ref *copy_ref(struct ref *ref)
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct ref *copy_ref_list(const struct ref *ref)
|
||||
{
|
||||
struct ref *ret = NULL;
|
||||
struct ref **tail = &ret;
|
||||
while (ref) {
|
||||
*tail = copy_ref(ref);
|
||||
ref = ref->next;
|
||||
tail = &((*tail)->next);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void free_refs(struct ref *ref)
|
||||
{
|
||||
struct ref *next;
|
||||
@@ -684,14 +696,6 @@ static int match_explicit_refs(struct ref *src, struct ref *dst,
|
||||
return -errs;
|
||||
}
|
||||
|
||||
static struct ref *find_ref_by_name(struct ref *list, const char *name)
|
||||
{
|
||||
for ( ; list; list = list->next)
|
||||
if (!strcmp(list->name, name))
|
||||
return list;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct refspec *check_pattern_match(const struct refspec *rs,
|
||||
int rs_nr,
|
||||
const struct ref *src)
|
||||
@@ -710,10 +714,12 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
|
||||
* without thinking.
|
||||
*/
|
||||
int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
|
||||
int nr_refspec, char **refspec, int all)
|
||||
int nr_refspec, const char **refspec, int flags)
|
||||
{
|
||||
struct refspec *rs =
|
||||
parse_ref_spec(nr_refspec, (const char **) refspec);
|
||||
int send_all = flags & MATCH_REFS_ALL;
|
||||
int send_mirror = flags & MATCH_REFS_MIRROR;
|
||||
|
||||
if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
|
||||
return -1;
|
||||
@@ -730,7 +736,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
|
||||
if (!pat)
|
||||
continue;
|
||||
}
|
||||
else if (prefixcmp(src->name, "refs/heads/"))
|
||||
else if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
|
||||
/*
|
||||
* "matching refs"; traditionally we pushed everything
|
||||
* including refs outside refs/heads/ hierarchy, but
|
||||
@@ -751,10 +757,13 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
|
||||
if (dst_peer && dst_peer->peer_ref)
|
||||
/* We're already sending something to this ref. */
|
||||
goto free_name;
|
||||
if (!dst_peer && !nr_refspec && !all)
|
||||
/* Remote doesn't have it, and we have no
|
||||
|
||||
if (!dst_peer && !nr_refspec && !(send_all || send_mirror))
|
||||
/*
|
||||
* Remote doesn't have it, and we have no
|
||||
* explicit pattern, and we don't have
|
||||
* --all. */
|
||||
* --all nor --mirror.
|
||||
*/
|
||||
goto free_name;
|
||||
if (!dst_peer) {
|
||||
/* Create a new one and link it */
|
||||
@@ -810,10 +819,10 @@ int branch_merge_matches(struct branch *branch,
|
||||
return ref_matches_abbrev(branch->merge[i]->src, refname);
|
||||
}
|
||||
|
||||
static struct ref *get_expanded_map(struct ref *remote_refs,
|
||||
static struct ref *get_expanded_map(const struct ref *remote_refs,
|
||||
const struct refspec *refspec)
|
||||
{
|
||||
struct ref *ref;
|
||||
const struct ref *ref;
|
||||
struct ref *ret = NULL;
|
||||
struct ref **tail = &ret;
|
||||
|
||||
@@ -824,7 +833,7 @@ static struct ref *get_expanded_map(struct ref *remote_refs,
|
||||
if (strchr(ref->name, '^'))
|
||||
continue; /* a dereference item */
|
||||
if (!prefixcmp(ref->name, refspec->src)) {
|
||||
char *match;
|
||||
const char *match;
|
||||
struct ref *cpy = copy_ref(ref);
|
||||
match = ref->name + remote_prefix_len;
|
||||
|
||||
@@ -842,9 +851,9 @@ static struct ref *get_expanded_map(struct ref *remote_refs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct ref *find_ref_by_name_abbrev(struct ref *refs, const char *name)
|
||||
static const struct ref *find_ref_by_name_abbrev(const struct ref *refs, const char *name)
|
||||
{
|
||||
struct ref *ref;
|
||||
const struct ref *ref;
|
||||
for (ref = refs; ref; ref = ref->next) {
|
||||
if (ref_matches_abbrev(name, ref->name))
|
||||
return ref;
|
||||
@@ -852,9 +861,9 @@ static struct ref *find_ref_by_name_abbrev(struct ref *refs, const char *name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ref *get_remote_ref(struct ref *remote_refs, const char *name)
|
||||
struct ref *get_remote_ref(const struct ref *remote_refs, const char *name)
|
||||
{
|
||||
struct ref *ref = find_ref_by_name_abbrev(remote_refs, name);
|
||||
const struct ref *ref = find_ref_by_name_abbrev(remote_refs, name);
|
||||
|
||||
if (!ref)
|
||||
return NULL;
|
||||
@@ -887,7 +896,7 @@ static struct ref *get_local_ref(const char *name)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int get_fetch_map(struct ref *remote_refs,
|
||||
int get_fetch_map(const struct ref *remote_refs,
|
||||
const struct refspec *refspec,
|
||||
struct ref ***tail,
|
||||
int missing_ok)
|
||||
|
||||
17
remote.h
17
remote.h
@@ -44,6 +44,10 @@ struct refspec {
|
||||
|
||||
struct ref *alloc_ref(unsigned namelen);
|
||||
|
||||
struct ref *copy_ref_list(const struct ref *ref);
|
||||
|
||||
int check_ref_type(const struct ref *ref, int flags);
|
||||
|
||||
/*
|
||||
* Frees the entire list and peers of elements.
|
||||
*/
|
||||
@@ -57,7 +61,7 @@ void ref_remove_duplicates(struct ref *ref_map);
|
||||
struct refspec *parse_ref_spec(int nr_refspec, const char **refspec);
|
||||
|
||||
int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
|
||||
int nr_refspec, char **refspec, int all);
|
||||
int nr_refspec, const char **refspec, int all);
|
||||
|
||||
/*
|
||||
* Given a list of the remote refs and the specification of things to
|
||||
@@ -71,10 +75,10 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
|
||||
* missing_ok is usually false, but when we are adding branch.$name.merge
|
||||
* it is Ok if the branch is not at the remote anymore.
|
||||
*/
|
||||
int get_fetch_map(struct ref *remote_refs, const struct refspec *refspec,
|
||||
int get_fetch_map(const struct ref *remote_refs, const struct refspec *refspec,
|
||||
struct ref ***tail, int missing_ok);
|
||||
|
||||
struct ref *get_remote_ref(struct ref *remote_refs, const char *name);
|
||||
struct ref *get_remote_ref(const struct ref *remote_refs, const char *name);
|
||||
|
||||
/*
|
||||
* For the given remote, reads the refspec's src and sets the other fields.
|
||||
@@ -98,4 +102,11 @@ struct branch *branch_get(const char *name);
|
||||
int branch_has_merge_config(struct branch *branch);
|
||||
int branch_merge_matches(struct branch *, int n, const char *);
|
||||
|
||||
/* Flags to match_refs. */
|
||||
enum match_refs_flags {
|
||||
MATCH_REFS_NONE = 0,
|
||||
MATCH_REFS_ALL = (1 << 0),
|
||||
MATCH_REFS_MIRROR = (1 << 1),
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
461
send-pack.c
461
send-pack.c
@@ -1,461 +0,0 @@
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
#include "tag.h"
|
||||
#include "refs.h"
|
||||
#include "pkt-line.h"
|
||||
#include "run-command.h"
|
||||
#include "remote.h"
|
||||
|
||||
static const char send_pack_usage[] =
|
||||
"git-send-pack [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
|
||||
" --all and explicit <ref> specification are mutually exclusive.";
|
||||
static const char *receivepack = "git-receive-pack";
|
||||
static int verbose;
|
||||
static int send_all;
|
||||
static int force_update;
|
||||
static int use_thin_pack;
|
||||
static int dry_run;
|
||||
|
||||
/*
|
||||
* Make a pack stream and spit it out into file descriptor fd
|
||||
*/
|
||||
static int pack_objects(int fd, struct ref *refs)
|
||||
{
|
||||
/*
|
||||
* The child becomes pack-objects --revs; we feed
|
||||
* the revision parameters to it via its stdin and
|
||||
* let its stdout go back to the other end.
|
||||
*/
|
||||
const char *args[] = {
|
||||
"pack-objects",
|
||||
"--all-progress",
|
||||
"--revs",
|
||||
"--stdout",
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
struct child_process po;
|
||||
|
||||
if (use_thin_pack)
|
||||
args[4] = "--thin";
|
||||
memset(&po, 0, sizeof(po));
|
||||
po.argv = args;
|
||||
po.in = -1;
|
||||
po.out = fd;
|
||||
po.git_cmd = 1;
|
||||
if (start_command(&po))
|
||||
die("git-pack-objects failed (%s)", strerror(errno));
|
||||
|
||||
/*
|
||||
* We feed the pack-objects we just spawned with revision
|
||||
* parameters by writing to the pipe.
|
||||
*/
|
||||
while (refs) {
|
||||
char buf[42];
|
||||
|
||||
if (!is_null_sha1(refs->old_sha1) &&
|
||||
has_sha1_file(refs->old_sha1)) {
|
||||
memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
|
||||
buf[0] = '^';
|
||||
buf[41] = '\n';
|
||||
if (!write_or_whine(po.in, buf, 42,
|
||||
"send-pack: send refs"))
|
||||
break;
|
||||
}
|
||||
if (!is_null_sha1(refs->new_sha1)) {
|
||||
memcpy(buf, sha1_to_hex(refs->new_sha1), 40);
|
||||
buf[40] = '\n';
|
||||
if (!write_or_whine(po.in, buf, 41,
|
||||
"send-pack: send refs"))
|
||||
break;
|
||||
}
|
||||
refs = refs->next;
|
||||
}
|
||||
|
||||
if (finish_command(&po))
|
||||
return error("pack-objects died with strange error");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unmark_and_free(struct commit_list *list, unsigned int mark)
|
||||
{
|
||||
while (list) {
|
||||
struct commit_list *temp = list;
|
||||
temp->item->object.flags &= ~mark;
|
||||
list = temp->next;
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
|
||||
static int ref_newer(const unsigned char *new_sha1,
|
||||
const unsigned char *old_sha1)
|
||||
{
|
||||
struct object *o;
|
||||
struct commit *old, *new;
|
||||
struct commit_list *list, *used;
|
||||
int found = 0;
|
||||
|
||||
/* Both new and old must be commit-ish and new is descendant of
|
||||
* old. Otherwise we require --force.
|
||||
*/
|
||||
o = deref_tag(parse_object(old_sha1), NULL, 0);
|
||||
if (!o || o->type != OBJ_COMMIT)
|
||||
return 0;
|
||||
old = (struct commit *) o;
|
||||
|
||||
o = deref_tag(parse_object(new_sha1), NULL, 0);
|
||||
if (!o || o->type != OBJ_COMMIT)
|
||||
return 0;
|
||||
new = (struct commit *) o;
|
||||
|
||||
if (parse_commit(new) < 0)
|
||||
return 0;
|
||||
|
||||
used = list = NULL;
|
||||
commit_list_insert(new, &list);
|
||||
while (list) {
|
||||
new = pop_most_recent_commit(&list, 1);
|
||||
commit_list_insert(new, &used);
|
||||
if (new == old) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
unmark_and_free(list, 1);
|
||||
unmark_and_free(used, 1);
|
||||
return found;
|
||||
}
|
||||
|
||||
static struct ref *local_refs, **local_tail;
|
||||
static struct ref *remote_refs, **remote_tail;
|
||||
|
||||
static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
|
||||
{
|
||||
struct ref *ref;
|
||||
int len = strlen(refname) + 1;
|
||||
ref = xcalloc(1, sizeof(*ref) + len);
|
||||
hashcpy(ref->new_sha1, sha1);
|
||||
memcpy(ref->name, refname, len);
|
||||
*local_tail = ref;
|
||||
local_tail = &ref->next;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_local_heads(void)
|
||||
{
|
||||
local_tail = &local_refs;
|
||||
for_each_ref(one_local_ref, NULL);
|
||||
}
|
||||
|
||||
static int receive_status(int in)
|
||||
{
|
||||
char line[1000];
|
||||
int ret = 0;
|
||||
int len = packet_read_line(in, line, sizeof(line));
|
||||
if (len < 10 || memcmp(line, "unpack ", 7)) {
|
||||
fprintf(stderr, "did not receive status back\n");
|
||||
return -1;
|
||||
}
|
||||
if (memcmp(line, "unpack ok\n", 10)) {
|
||||
fputs(line, stderr);
|
||||
ret = -1;
|
||||
}
|
||||
while (1) {
|
||||
len = packet_read_line(in, line, sizeof(line));
|
||||
if (!len)
|
||||
break;
|
||||
if (len < 3 ||
|
||||
(memcmp(line, "ok", 2) && memcmp(line, "ng", 2))) {
|
||||
fprintf(stderr, "protocol error: %s\n", line);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
if (!memcmp(line, "ok", 2))
|
||||
continue;
|
||||
fputs(line, stderr);
|
||||
ret = -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void update_tracking_ref(struct remote *remote, struct ref *ref)
|
||||
{
|
||||
struct refspec rs;
|
||||
int will_delete_ref;
|
||||
|
||||
rs.src = ref->name;
|
||||
rs.dst = NULL;
|
||||
|
||||
if (!ref->peer_ref)
|
||||
return;
|
||||
|
||||
will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
|
||||
|
||||
if (!will_delete_ref &&
|
||||
!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1))
|
||||
return;
|
||||
|
||||
if (!remote_find_tracking(remote, &rs)) {
|
||||
fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
|
||||
if (is_null_sha1(ref->peer_ref->new_sha1)) {
|
||||
if (delete_ref(rs.dst, NULL))
|
||||
error("Failed to delete");
|
||||
} else
|
||||
update_ref("update by push", rs.dst,
|
||||
ref->new_sha1, NULL, 0, 0);
|
||||
free(rs.dst);
|
||||
}
|
||||
}
|
||||
|
||||
static int send_pack(int in, int out, struct remote *remote, int nr_refspec, char **refspec)
|
||||
{
|
||||
struct ref *ref;
|
||||
int new_refs;
|
||||
int ret = 0;
|
||||
int ask_for_status_report = 0;
|
||||
int allow_deleting_refs = 0;
|
||||
int expect_status_report = 0;
|
||||
|
||||
/* No funny business with the matcher */
|
||||
remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL);
|
||||
get_local_heads();
|
||||
|
||||
/* Does the other end support the reporting? */
|
||||
if (server_supports("report-status"))
|
||||
ask_for_status_report = 1;
|
||||
if (server_supports("delete-refs"))
|
||||
allow_deleting_refs = 1;
|
||||
|
||||
/* match them up */
|
||||
if (!remote_tail)
|
||||
remote_tail = &remote_refs;
|
||||
if (match_refs(local_refs, remote_refs, &remote_tail,
|
||||
nr_refspec, refspec, send_all))
|
||||
return -1;
|
||||
|
||||
if (!remote_refs) {
|
||||
fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
|
||||
"Perhaps you should specify a branch such as 'master'.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, tell the other end!
|
||||
*/
|
||||
new_refs = 0;
|
||||
for (ref = remote_refs; ref; ref = ref->next) {
|
||||
char old_hex[60], *new_hex;
|
||||
int will_delete_ref;
|
||||
|
||||
if (!ref->peer_ref)
|
||||
continue;
|
||||
|
||||
|
||||
will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
|
||||
if (will_delete_ref && !allow_deleting_refs) {
|
||||
error("remote does not support deleting refs");
|
||||
ret = -2;
|
||||
continue;
|
||||
}
|
||||
if (!will_delete_ref &&
|
||||
!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
|
||||
if (verbose)
|
||||
fprintf(stderr, "'%s': up-to-date\n", ref->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* This part determines what can overwrite what.
|
||||
* The rules are:
|
||||
*
|
||||
* (0) you can always use --force or +A:B notation to
|
||||
* selectively force individual ref pairs.
|
||||
*
|
||||
* (1) if the old thing does not exist, it is OK.
|
||||
*
|
||||
* (2) if you do not have the old thing, you are not allowed
|
||||
* to overwrite it; you would not know what you are losing
|
||||
* otherwise.
|
||||
*
|
||||
* (3) if both new and old are commit-ish, and new is a
|
||||
* descendant of old, it is OK.
|
||||
*
|
||||
* (4) regardless of all of the above, removing :B is
|
||||
* always allowed.
|
||||
*/
|
||||
|
||||
if (!force_update &&
|
||||
!will_delete_ref &&
|
||||
!is_null_sha1(ref->old_sha1) &&
|
||||
!ref->force) {
|
||||
if (!has_sha1_file(ref->old_sha1) ||
|
||||
!ref_newer(ref->peer_ref->new_sha1,
|
||||
ref->old_sha1)) {
|
||||
/* We do not have the remote ref, or
|
||||
* we know that the remote ref is not
|
||||
* an ancestor of what we are trying to
|
||||
* push. Either way this can be losing
|
||||
* commits at the remote end and likely
|
||||
* we were not up to date to begin with.
|
||||
*/
|
||||
error("remote '%s' is not an ancestor of\n"
|
||||
" local '%s'.\n"
|
||||
" Maybe you are not up-to-date and "
|
||||
"need to pull first?",
|
||||
ref->name,
|
||||
ref->peer_ref->name);
|
||||
ret = -2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
|
||||
if (!will_delete_ref)
|
||||
new_refs++;
|
||||
strcpy(old_hex, sha1_to_hex(ref->old_sha1));
|
||||
new_hex = sha1_to_hex(ref->new_sha1);
|
||||
|
||||
if (!dry_run) {
|
||||
if (ask_for_status_report) {
|
||||
packet_write(out, "%s %s %s%c%s",
|
||||
old_hex, new_hex, ref->name, 0,
|
||||
"report-status");
|
||||
ask_for_status_report = 0;
|
||||
expect_status_report = 1;
|
||||
}
|
||||
else
|
||||
packet_write(out, "%s %s %s",
|
||||
old_hex, new_hex, ref->name);
|
||||
}
|
||||
if (will_delete_ref)
|
||||
fprintf(stderr, "deleting '%s'\n", ref->name);
|
||||
else {
|
||||
fprintf(stderr, "updating '%s'", ref->name);
|
||||
if (strcmp(ref->name, ref->peer_ref->name))
|
||||
fprintf(stderr, " using '%s'",
|
||||
ref->peer_ref->name);
|
||||
fprintf(stderr, "\n from %s\n to %s\n",
|
||||
old_hex, new_hex);
|
||||
}
|
||||
}
|
||||
|
||||
packet_flush(out);
|
||||
if (new_refs && !dry_run)
|
||||
ret = pack_objects(out, remote_refs);
|
||||
close(out);
|
||||
|
||||
if (expect_status_report) {
|
||||
if (receive_status(in))
|
||||
ret = -4;
|
||||
}
|
||||
|
||||
if (!dry_run && remote && ret == 0) {
|
||||
for (ref = remote_refs; ref; ref = ref->next)
|
||||
update_tracking_ref(remote, ref);
|
||||
}
|
||||
|
||||
if (!new_refs && ret == 0)
|
||||
fprintf(stderr, "Everything up-to-date\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void verify_remote_names(int nr_heads, char **heads)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_heads; i++) {
|
||||
const char *remote = strchr(heads[i], ':');
|
||||
|
||||
remote = remote ? (remote + 1) : heads[i];
|
||||
switch (check_ref_format(remote)) {
|
||||
case 0: /* ok */
|
||||
case -2: /* ok but a single level -- that is fine for
|
||||
* a match pattern.
|
||||
*/
|
||||
case -3: /* ok but ends with a pattern-match character */
|
||||
continue;
|
||||
}
|
||||
die("remote part of refspec is not a valid name in %s",
|
||||
heads[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i, nr_heads = 0;
|
||||
char *dest = NULL;
|
||||
char **heads = NULL;
|
||||
int fd[2], ret;
|
||||
struct child_process *conn;
|
||||
char *remote_name = NULL;
|
||||
struct remote *remote = NULL;
|
||||
|
||||
setup_git_directory();
|
||||
git_config(git_default_config);
|
||||
|
||||
argv++;
|
||||
for (i = 1; i < argc; i++, argv++) {
|
||||
char *arg = *argv;
|
||||
|
||||
if (*arg == '-') {
|
||||
if (!prefixcmp(arg, "--receive-pack=")) {
|
||||
receivepack = arg + 15;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--exec=")) {
|
||||
receivepack = arg + 7;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--remote=")) {
|
||||
remote_name = arg + 9;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--all")) {
|
||||
send_all = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--dry-run")) {
|
||||
dry_run = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--force")) {
|
||||
force_update = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--verbose")) {
|
||||
verbose = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--thin")) {
|
||||
use_thin_pack = 1;
|
||||
continue;
|
||||
}
|
||||
usage(send_pack_usage);
|
||||
}
|
||||
if (!dest) {
|
||||
dest = arg;
|
||||
continue;
|
||||
}
|
||||
heads = argv;
|
||||
nr_heads = argc - i;
|
||||
break;
|
||||
}
|
||||
if (!dest)
|
||||
usage(send_pack_usage);
|
||||
if (heads && send_all)
|
||||
usage(send_pack_usage);
|
||||
verify_remote_names(nr_heads, heads);
|
||||
|
||||
if (remote_name) {
|
||||
remote = remote_get(remote_name);
|
||||
if (!remote_has_url(remote, dest)) {
|
||||
die("Destination %s is not a uri for %s",
|
||||
dest, remote_name);
|
||||
}
|
||||
}
|
||||
|
||||
conn = git_connect(fd, dest, receivepack, verbose ? CONNECT_VERBOSE : 0);
|
||||
ret = send_pack(fd[0], fd[1], remote, nr_heads, heads);
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
ret |= finish_connect(conn);
|
||||
return !!ret;
|
||||
}
|
||||
18
send-pack.h
Normal file
18
send-pack.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef SEND_PACK_H
|
||||
#define SEND_PACK_H
|
||||
|
||||
struct send_pack_args {
|
||||
const char *receivepack;
|
||||
unsigned verbose:1,
|
||||
send_all:1,
|
||||
send_mirror:1,
|
||||
force_update:1,
|
||||
use_thin_pack:1,
|
||||
dry_run:1;
|
||||
};
|
||||
|
||||
int send_pack(struct send_pack_args *args,
|
||||
const char *dest, struct remote *remote,
|
||||
int nr_heads, const char **heads);
|
||||
|
||||
#endif
|
||||
@@ -35,7 +35,7 @@ static int update_info_refs(int force)
|
||||
safe_create_leading_directories(path0);
|
||||
info_ref_fp = fopen(path1, "w");
|
||||
if (!info_ref_fp)
|
||||
return error("unable to update %s", path0);
|
||||
return error("unable to update %s", path1);
|
||||
for_each_ref(add_info_ref, NULL);
|
||||
fclose(info_ref_fp);
|
||||
adjust_shared_perm(path1);
|
||||
|
||||
2
setup.c
2
setup.c
@@ -84,7 +84,7 @@ const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
|
||||
static char path[PATH_MAX];
|
||||
char *p;
|
||||
#ifndef __MINGW32__
|
||||
if (!pfx || !*pfx || arg[0] == '/')
|
||||
if (!pfx || !*pfx || is_absolute_path(arg))
|
||||
return arg;
|
||||
#else
|
||||
/* don't add prefix to absolute paths */
|
||||
|
||||
@@ -36,7 +36,6 @@ test_expect_failure 'rebase with git am -3 (default)' '
|
||||
'
|
||||
|
||||
test_expect_success 'rebase --skip with am -3' '
|
||||
git reset --hard HEAD &&
|
||||
git rebase --skip
|
||||
'
|
||||
|
||||
@@ -57,7 +56,6 @@ test_expect_success 'checkout skip-merge' 'git checkout -f skip-merge'
|
||||
test_expect_failure 'rebase with --merge' 'git rebase --merge master'
|
||||
|
||||
test_expect_success 'rebase --skip with --merge' '
|
||||
git reset --hard HEAD &&
|
||||
git rebase --skip
|
||||
'
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ cat path0 >path1
|
||||
chmod +x path1
|
||||
|
||||
test_expect_success \
|
||||
'update-cache --add two files with and without +x.' \
|
||||
'update-index --add two files with and without +x.' \
|
||||
'git update-index --add path0 path1'
|
||||
|
||||
mv path0 path0-
|
||||
|
||||
@@ -27,7 +27,7 @@ Line 15
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'update-cache --add a file.' \
|
||||
'update-index --add a file.' \
|
||||
'git update-index --add path0'
|
||||
|
||||
test_expect_success \
|
||||
|
||||
@@ -90,7 +90,7 @@ diff --git a/Documentation/git.txt b/Documentation/git.txt
|
||||
diff --git a/Makefile b/Makefile
|
||||
--- a/Makefile
|
||||
+++ b/Makefile
|
||||
@@ -30,7 +30,7 @@ PROG= git-update-cache git-diff-files
|
||||
@@ -30,7 +30,7 @@ PROG= git-update-index git-diff-files
|
||||
git-checkout-cache git-diff-tree git-rev-tree git-ls-files \
|
||||
git-check-files git-ls-tree git-merge-base git-merge-cache \
|
||||
git-unpack-file git-export git-diff-cache git-convert-cache \
|
||||
|
||||
@@ -9,7 +9,7 @@ diff --git a/Makefile b/Makefile
|
||||
- git-deltafy-script
|
||||
+ git-deltafy-script git-fetch-script
|
||||
|
||||
PROG= git-update-cache git-diff-files git-init-db git-write-tree \
|
||||
PROG= git-update-index git-diff-files git-init-db git-write-tree \
|
||||
git-read-tree git-commit-tree git-cat-file git-fsck-cache \
|
||||
diff --git a/git-pull-script b/git-fetch-script
|
||||
similarity index 87%
|
||||
|
||||
@@ -200,7 +200,7 @@ diff a/Documentation/git.txt b/Documentation/git.txt
|
||||
diff a/Makefile b/Makefile
|
||||
--- a/Makefile
|
||||
+++ b/Makefile
|
||||
@@ -30,7 +30,7 @@ PROG= git-update-cache git-diff-files
|
||||
@@ -30,7 +30,7 @@ PROG= git-update-index git-diff-files
|
||||
git-checkout-cache git-diff-tree git-rev-tree git-ls-files \
|
||||
git-check-files git-ls-tree git-merge-base git-merge-cache \
|
||||
git-unpack-file git-export git-diff-cache git-convert-cache \
|
||||
|
||||
@@ -8,7 +8,7 @@ diff a/Makefile b/Makefile
|
||||
- git-deltafy-script
|
||||
+ git-deltafy-script git-fetch-script
|
||||
|
||||
PROG= git-update-cache git-diff-files git-init-db git-write-tree \
|
||||
PROG= git-update-index git-diff-files git-init-db git-write-tree \
|
||||
git-read-tree git-commit-tree git-cat-file git-fsck-cache \
|
||||
diff a/git-fetch-script b/git-fetch-script
|
||||
--- /dev/null
|
||||
|
||||
@@ -24,7 +24,7 @@ cat >gpatch.file <<\EOF &&
|
||||
+++ file1+ 2007-02-21 01:07:44.000000000 -0800
|
||||
@@ -1 +1 @@
|
||||
-A
|
||||
+B
|
||||
+B
|
||||
EOF
|
||||
|
||||
sed -e 's|file1|sub/&|' gpatch.file >gpatch-sub.file &&
|
||||
|
||||
53
t/t5404-tracking-branches.sh
Executable file
53
t/t5404-tracking-branches.sh
Executable file
@@ -0,0 +1,53 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='tracking branch update checks for git push'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
echo 1 >file &&
|
||||
git add file &&
|
||||
git commit -m 1 &&
|
||||
git branch b1 &&
|
||||
git branch b2 &&
|
||||
git clone . aa &&
|
||||
git checkout b1 &&
|
||||
echo b1 >>file &&
|
||||
git commit -a -m b1 &&
|
||||
git checkout b2 &&
|
||||
echo b2 >>file &&
|
||||
git commit -a -m b2
|
||||
'
|
||||
|
||||
test_expect_success 'prepare pushable branches' '
|
||||
cd aa &&
|
||||
b1=$(git rev-parse origin/b1) &&
|
||||
b2=$(git rev-parse origin/b2) &&
|
||||
git checkout -b b1 origin/b1 &&
|
||||
echo aa-b1 >>file &&
|
||||
git commit -a -m aa-b1 &&
|
||||
git checkout -b b2 origin/b2 &&
|
||||
echo aa-b2 >>file &&
|
||||
git commit -a -m aa-b2 &&
|
||||
git checkout master &&
|
||||
echo aa-master >>file &&
|
||||
git commit -a -m aa-master
|
||||
'
|
||||
|
||||
test_expect_success 'mixed-success push returns error' '! git push'
|
||||
|
||||
test_expect_success 'check tracking branches updated correctly after push' '
|
||||
test "$(git rev-parse origin/master)" = "$(git rev-parse master)"
|
||||
'
|
||||
|
||||
test_expect_success 'check tracking branches not updated for failed refs' '
|
||||
test "$(git rev-parse origin/b1)" = "$b1" &&
|
||||
test "$(git rev-parse origin/b2)" = "$b2"
|
||||
'
|
||||
|
||||
test_expect_success 'deleted branches have their tracking branches removed' '
|
||||
git push origin :b1 &&
|
||||
test "$(git rev-parse origin/b1)" = "origin/b1"
|
||||
'
|
||||
|
||||
test_done
|
||||
42
t/t5405-send-pack-rewind.sh
Executable file
42
t/t5405-send-pack-rewind.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='forced push to replace commit we do not have'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success setup '
|
||||
|
||||
>file1 && git add file1 && test_tick &&
|
||||
git commit -m Initial &&
|
||||
|
||||
mkdir another && (
|
||||
cd another &&
|
||||
git init &&
|
||||
git fetch .. master:master
|
||||
) &&
|
||||
|
||||
>file2 && git add file2 && test_tick &&
|
||||
git commit -m Second
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'non forced push should die not segfault' '
|
||||
|
||||
(
|
||||
cd another &&
|
||||
git push .. master:master
|
||||
test $? = 1
|
||||
)
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'forced push should succeed' '
|
||||
|
||||
(
|
||||
cd another &&
|
||||
git push .. +master:master
|
||||
)
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
24
t/t5406-remote-rejects.sh
Executable file
24
t/t5406-remote-rejects.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='remote push rejects are reported by client'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
mkdir .git/hooks &&
|
||||
(echo "#!/bin/sh" ; echo "exit 1") >.git/hooks/update &&
|
||||
chmod +x .git/hooks/update &&
|
||||
echo 1 >file &&
|
||||
git add file &&
|
||||
git commit -m 1 &&
|
||||
git clone . child &&
|
||||
cd child &&
|
||||
echo 2 >file &&
|
||||
git commit -a -m 2
|
||||
'
|
||||
|
||||
test_expect_success 'push reports error' '! git push 2>stderr'
|
||||
|
||||
test_expect_success 'individual ref reports error' 'grep rejected stderr'
|
||||
|
||||
test_done
|
||||
52
t/t5512-ls-remote.sh
Executable file
52
t/t5512-ls-remote.sh
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git ls-remote'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success setup '
|
||||
|
||||
>file &&
|
||||
git add file &&
|
||||
test_tick &&
|
||||
git commit -m initial &&
|
||||
git tag mark &&
|
||||
git show-ref --tags -d | sed -e "s/ / /" >expected.tag &&
|
||||
(
|
||||
echo "$(git rev-parse HEAD) HEAD"
|
||||
git show-ref -d | sed -e "s/ / /"
|
||||
) >expected.all &&
|
||||
|
||||
git remote add self $(pwd)/.git
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'ls-remote --tags .git' '
|
||||
|
||||
git ls-remote --tags .git >actual &&
|
||||
diff -u expected.tag actual
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'ls-remote .git' '
|
||||
|
||||
git ls-remote .git >actual &&
|
||||
diff -u expected.all actual
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'ls-remote --tags self' '
|
||||
|
||||
git ls-remote --tags self >actual &&
|
||||
diff -u expected.tag actual
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'ls-remote self' '
|
||||
|
||||
git ls-remote self >actual &&
|
||||
diff -u expected.all actual
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -247,9 +247,8 @@ test_expect_success 'push with colon-less refspec (4)' '
|
||||
test_expect_success 'push with dry-run' '
|
||||
|
||||
mk_test heads/master &&
|
||||
cd testrepo &&
|
||||
old_commit=$(git show-ref -s --verify refs/heads/master) &&
|
||||
cd .. &&
|
||||
(cd testrepo &&
|
||||
old_commit=$(git show-ref -s --verify refs/heads/master)) &&
|
||||
git push --dry-run testrepo &&
|
||||
check_push_result $old_commit heads/master
|
||||
'
|
||||
@@ -257,28 +256,40 @@ test_expect_success 'push with dry-run' '
|
||||
test_expect_success 'push updates local refs' '
|
||||
|
||||
rm -rf parent child &&
|
||||
mkdir parent && cd parent && git init &&
|
||||
echo one >foo && git add foo && git commit -m one &&
|
||||
cd .. &&
|
||||
git clone parent child && cd child &&
|
||||
mkdir parent &&
|
||||
(cd parent && git init &&
|
||||
echo one >foo && git add foo && git commit -m one) &&
|
||||
git clone parent child &&
|
||||
(cd child &&
|
||||
echo two >foo && git commit -a -m two &&
|
||||
git push &&
|
||||
test $(git rev-parse master) = $(git rev-parse remotes/origin/master)
|
||||
test $(git rev-parse master) = $(git rev-parse remotes/origin/master))
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'push does not update local refs on failure' '
|
||||
|
||||
rm -rf parent child &&
|
||||
mkdir parent && cd parent && git init &&
|
||||
mkdir parent &&
|
||||
(cd parent && git init &&
|
||||
echo one >foo && git add foo && git commit -m one &&
|
||||
echo exit 1 >.git/hooks/pre-receive &&
|
||||
chmod +x .git/hooks/pre-receive &&
|
||||
cd .. &&
|
||||
git clone parent child && cd child &&
|
||||
echo two >foo && git commit -a -m two || exit 1
|
||||
git push && exit 1
|
||||
test $(git rev-parse master) != $(git rev-parse remotes/origin/master)
|
||||
chmod +x .git/hooks/pre-receive) &&
|
||||
git clone parent child &&
|
||||
(cd child &&
|
||||
echo two >foo && git commit -a -m two &&
|
||||
! git push &&
|
||||
test $(git rev-parse master) != \
|
||||
$(git rev-parse remotes/origin/master))
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'allow deleting an invalid remote ref' '
|
||||
|
||||
pwd &&
|
||||
rm -f testrepo/.git/objects/??/* &&
|
||||
git push testrepo :refs/heads/master &&
|
||||
(cd testrepo && ! git rev-parse --verify refs/heads/master)
|
||||
|
||||
'
|
||||
|
||||
|
||||
228
t/t5517-push-mirror.sh
Executable file
228
t/t5517-push-mirror.sh
Executable file
@@ -0,0 +1,228 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='pushing to a mirror repository'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
D=`pwd`
|
||||
|
||||
invert () {
|
||||
if "$@"; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
mk_repo_pair () {
|
||||
rm -rf master mirror &&
|
||||
mkdir mirror &&
|
||||
(
|
||||
cd mirror &&
|
||||
git init
|
||||
) &&
|
||||
mkdir master &&
|
||||
(
|
||||
cd master &&
|
||||
git init &&
|
||||
git config remote.up.url ../mirror
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
# BRANCH tests
|
||||
test_expect_success 'push mirror creates new branches' '
|
||||
|
||||
mk_repo_pair &&
|
||||
(
|
||||
cd master &&
|
||||
echo one >foo && git add foo && git commit -m one &&
|
||||
git push --mirror up
|
||||
) &&
|
||||
master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
|
||||
mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
|
||||
test "$master_master" = "$mirror_master"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'push mirror updates existing branches' '
|
||||
|
||||
mk_repo_pair &&
|
||||
(
|
||||
cd master &&
|
||||
echo one >foo && git add foo && git commit -m one &&
|
||||
git push --mirror up &&
|
||||
echo two >foo && git add foo && git commit -m two &&
|
||||
git push --mirror up
|
||||
) &&
|
||||
master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
|
||||
mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
|
||||
test "$master_master" = "$mirror_master"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'push mirror force updates existing branches' '
|
||||
|
||||
mk_repo_pair &&
|
||||
(
|
||||
cd master &&
|
||||
echo one >foo && git add foo && git commit -m one &&
|
||||
git push --mirror up &&
|
||||
echo two >foo && git add foo && git commit -m two &&
|
||||
git push --mirror up &&
|
||||
git reset --hard HEAD^
|
||||
git push --mirror up
|
||||
) &&
|
||||
master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
|
||||
mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
|
||||
test "$master_master" = "$mirror_master"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'push mirror removes branches' '
|
||||
|
||||
mk_repo_pair &&
|
||||
(
|
||||
cd master &&
|
||||
echo one >foo && git add foo && git commit -m one &&
|
||||
git branch remove master &&
|
||||
git push --mirror up &&
|
||||
git branch -D remove
|
||||
git push --mirror up
|
||||
) &&
|
||||
(
|
||||
cd mirror &&
|
||||
invert git show-ref -s --verify refs/heads/remove
|
||||
)
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'push mirror adds, updates and removes branches together' '
|
||||
|
||||
mk_repo_pair &&
|
||||
(
|
||||
cd master &&
|
||||
echo one >foo && git add foo && git commit -m one &&
|
||||
git branch remove master &&
|
||||
git push --mirror up &&
|
||||
git branch -D remove &&
|
||||
git branch add master &&
|
||||
echo two >foo && git add foo && git commit -m two &&
|
||||
git push --mirror up
|
||||
) &&
|
||||
master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
|
||||
master_add=$(cd master && git show-ref -s --verify refs/heads/add) &&
|
||||
mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
|
||||
mirror_add=$(cd mirror && git show-ref -s --verify refs/heads/add) &&
|
||||
test "$master_master" = "$mirror_master" &&
|
||||
test "$master_add" = "$mirror_add" &&
|
||||
(
|
||||
cd mirror &&
|
||||
invert git show-ref -s --verify refs/heads/remove
|
||||
)
|
||||
|
||||
'
|
||||
|
||||
|
||||
# TAG tests
|
||||
test_expect_success 'push mirror creates new tags' '
|
||||
|
||||
mk_repo_pair &&
|
||||
(
|
||||
cd master &&
|
||||
echo one >foo && git add foo && git commit -m one &&
|
||||
git tag -f tmaster master &&
|
||||
git push --mirror up
|
||||
) &&
|
||||
master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
|
||||
mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
|
||||
test "$master_master" = "$mirror_master"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'push mirror updates existing tags' '
|
||||
|
||||
mk_repo_pair &&
|
||||
(
|
||||
cd master &&
|
||||
echo one >foo && git add foo && git commit -m one &&
|
||||
git tag -f tmaster master &&
|
||||
git push --mirror up &&
|
||||
echo two >foo && git add foo && git commit -m two &&
|
||||
git tag -f tmaster master &&
|
||||
git push --mirror up
|
||||
) &&
|
||||
master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
|
||||
mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
|
||||
test "$master_master" = "$mirror_master"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'push mirror force updates existing tags' '
|
||||
|
||||
mk_repo_pair &&
|
||||
(
|
||||
cd master &&
|
||||
echo one >foo && git add foo && git commit -m one &&
|
||||
git tag -f tmaster master &&
|
||||
git push --mirror up &&
|
||||
echo two >foo && git add foo && git commit -m two &&
|
||||
git tag -f tmaster master &&
|
||||
git push --mirror up &&
|
||||
git reset --hard HEAD^
|
||||
git tag -f tmaster master &&
|
||||
git push --mirror up
|
||||
) &&
|
||||
master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
|
||||
mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
|
||||
test "$master_master" = "$mirror_master"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'push mirror removes tags' '
|
||||
|
||||
mk_repo_pair &&
|
||||
(
|
||||
cd master &&
|
||||
echo one >foo && git add foo && git commit -m one &&
|
||||
git tag -f tremove master &&
|
||||
git push --mirror up &&
|
||||
git tag -d tremove
|
||||
git push --mirror up
|
||||
) &&
|
||||
(
|
||||
cd mirror &&
|
||||
invert git show-ref -s --verify refs/tags/tremove
|
||||
)
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'push mirror adds, updates and removes tags together' '
|
||||
|
||||
mk_repo_pair &&
|
||||
(
|
||||
cd master &&
|
||||
echo one >foo && git add foo && git commit -m one &&
|
||||
git tag -f tmaster master &&
|
||||
git tag -f tremove master &&
|
||||
git push --mirror up &&
|
||||
git tag -d tremove &&
|
||||
git tag tadd master &&
|
||||
echo two >foo && git add foo && git commit -m two &&
|
||||
git tag -f tmaster master &&
|
||||
git push --mirror up
|
||||
) &&
|
||||
master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
|
||||
master_add=$(cd master && git show-ref -s --verify refs/tags/tadd) &&
|
||||
mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
|
||||
mirror_add=$(cd mirror && git show-ref -s --verify refs/tags/tadd) &&
|
||||
test "$master_master" = "$mirror_master" &&
|
||||
test "$master_add" = "$mirror_add" &&
|
||||
(
|
||||
cd mirror &&
|
||||
invert git show-ref -s --verify refs/tags/tremove
|
||||
)
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -71,6 +71,43 @@ test_expect_success 'bisect start with one bad and good' '
|
||||
git bisect next
|
||||
'
|
||||
|
||||
test_expect_success 'bisect reset: back in the master branch' '
|
||||
git bisect reset &&
|
||||
echo "* master" > branch.expect &&
|
||||
git branch > branch.output &&
|
||||
cmp branch.expect branch.output
|
||||
'
|
||||
|
||||
test_expect_success 'bisect reset: back in another branch' '
|
||||
git checkout -b other &&
|
||||
git bisect start &&
|
||||
git bisect good $HASH1 &&
|
||||
git bisect bad $HASH3 &&
|
||||
git bisect reset &&
|
||||
echo " master" > branch.expect &&
|
||||
echo "* other" >> branch.expect &&
|
||||
git branch > branch.output &&
|
||||
cmp branch.expect branch.output
|
||||
'
|
||||
|
||||
test_expect_success 'bisect reset when not bisecting' '
|
||||
git bisect reset &&
|
||||
git branch > branch.output &&
|
||||
cmp branch.expect branch.output
|
||||
'
|
||||
|
||||
test_expect_success 'bisect reset removes packed refs' '
|
||||
git bisect reset &&
|
||||
git bisect start &&
|
||||
git bisect good $HASH1 &&
|
||||
git bisect bad $HASH3 &&
|
||||
git pack-refs --all --prune &&
|
||||
git bisect next &&
|
||||
git bisect reset &&
|
||||
test -z "$(git for-each-ref "refs/bisect/*")" &&
|
||||
test -z "$(git for-each-ref "refs/heads/bisect")"
|
||||
'
|
||||
|
||||
# $HASH1 is good, $HASH4 is bad, we skip $HASH3
|
||||
# but $HASH2 is bad,
|
||||
# so we should find $HASH2 as the first bad commit
|
||||
@@ -167,7 +204,7 @@ test_expect_success 'bisect skip: add line and then a new test' '
|
||||
git bisect skip &&
|
||||
git bisect good > my_bisect_log.txt &&
|
||||
grep "$HASH5 is first bad commit" my_bisect_log.txt &&
|
||||
git bisect log > log_to_replay.txt
|
||||
git bisect log > log_to_replay.txt &&
|
||||
git bisect reset
|
||||
'
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ test_expect_success 'use index-filter to move into a subdirectory' '
|
||||
|
||||
test_expect_success 'stops when msg filter fails' '
|
||||
old=$(git rev-parse HEAD) &&
|
||||
! git-filter-branch -f --msg-filter false &&
|
||||
! git-filter-branch -f --msg-filter false HEAD &&
|
||||
test $old = $(git rev-parse HEAD) &&
|
||||
rm -rf .git-rewrite
|
||||
'
|
||||
|
||||
122
t/t9600-cvsimport.sh
Executable file
122
t/t9600-cvsimport.sh
Executable file
@@ -0,0 +1,122 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git-cvsimport basic tests'
|
||||
. ./test-lib.sh
|
||||
|
||||
if ! ( type cvs && type cvsps ) >/dev/null 2>&1
|
||||
then
|
||||
test_expect_success 'skipping cvsimport tests, cvs/cvsps not found' ''
|
||||
test_done
|
||||
exit
|
||||
fi
|
||||
|
||||
CVSROOT=$(pwd)/cvsroot
|
||||
export CVSROOT
|
||||
# for clean cvsps cache
|
||||
HOME=$(pwd)
|
||||
export HOME
|
||||
|
||||
test_expect_success 'setup cvsroot' 'cvs init'
|
||||
|
||||
test_expect_success 'setup a cvs module' '
|
||||
|
||||
mkdir $CVSROOT/module &&
|
||||
cvs co -d module-cvs module &&
|
||||
cd module-cvs &&
|
||||
cat <<EOF >o_fortuna &&
|
||||
O Fortuna
|
||||
velut luna
|
||||
statu variabilis,
|
||||
|
||||
semper crescis
|
||||
aut decrescis;
|
||||
vita detestabilis
|
||||
|
||||
nunc obdurat
|
||||
et tunc curat
|
||||
ludo mentis aciem,
|
||||
|
||||
egestatem,
|
||||
potestatem
|
||||
dissolvit ut glaciem.
|
||||
EOF
|
||||
cvs add o_fortuna &&
|
||||
cat <<EOF >message &&
|
||||
add "O Fortuna" lyrics
|
||||
|
||||
These public domain lyrics make an excellent sample text.
|
||||
EOF
|
||||
cvs commit -F message &&
|
||||
cd ..
|
||||
'
|
||||
|
||||
test_expect_success 'import a trivial module' '
|
||||
|
||||
git cvsimport -a -z 0 -C module-git module &&
|
||||
git diff module-cvs/o_fortuna module-git/o_fortuna
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'pack refs' 'cd module-git && git gc && cd ..'
|
||||
|
||||
test_expect_success 'update cvs module' '
|
||||
|
||||
cd module-cvs &&
|
||||
cat <<EOF >o_fortuna &&
|
||||
O Fortune,
|
||||
like the moon
|
||||
you are changeable,
|
||||
|
||||
ever waxing
|
||||
and waning;
|
||||
hateful life
|
||||
|
||||
first oppresses
|
||||
and then soothes
|
||||
as fancy takes it;
|
||||
|
||||
poverty
|
||||
and power
|
||||
it melts them like ice.
|
||||
EOF
|
||||
cat <<EOF >message &&
|
||||
translate to English
|
||||
|
||||
My Latin is terrible.
|
||||
EOF
|
||||
cvs commit -F message &&
|
||||
cd ..
|
||||
'
|
||||
|
||||
test_expect_success 'update git module' '
|
||||
|
||||
cd module-git &&
|
||||
git cvsimport -a -z 0 module &&
|
||||
git merge origin &&
|
||||
cd .. &&
|
||||
git diff module-cvs/o_fortuna module-git/o_fortuna
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'update cvs module' '
|
||||
|
||||
cd module-cvs &&
|
||||
echo 1 >tick &&
|
||||
cvs add tick &&
|
||||
cvs commit -m 1
|
||||
cd ..
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'cvsimport.module config works' '
|
||||
|
||||
cd module-git &&
|
||||
git config cvsimport.module module &&
|
||||
git cvsimport -a -z0 &&
|
||||
git merge origin &&
|
||||
cd .. &&
|
||||
git diff module-cvs/tick module-git/tick
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
70
transport.c
70
transport.c
@@ -6,6 +6,7 @@
|
||||
#endif
|
||||
#include "pkt-line.h"
|
||||
#include "fetch-pack.h"
|
||||
#include "send-pack.h"
|
||||
#include "walker.h"
|
||||
#include "bundle.h"
|
||||
#include "dir.h"
|
||||
@@ -141,7 +142,7 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list)
|
||||
}
|
||||
}
|
||||
|
||||
static struct ref *get_refs_via_rsync(const struct transport *transport)
|
||||
static struct ref *get_refs_via_rsync(struct transport *transport)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
|
||||
struct ref dummy, *tail = &dummy;
|
||||
@@ -283,6 +284,9 @@ static int rsync_transport_push(struct transport *transport,
|
||||
struct child_process rsync;
|
||||
const char *args[10];
|
||||
|
||||
if (flags & TRANSPORT_PUSH_MIRROR)
|
||||
return error("rsync transport does not support mirror mode");
|
||||
|
||||
/* first push the objects */
|
||||
|
||||
strbuf_addstr(&buf, transport->url);
|
||||
@@ -388,6 +392,9 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons
|
||||
int argc;
|
||||
int err;
|
||||
|
||||
if (flags & TRANSPORT_PUSH_MIRROR)
|
||||
return error("http transport does not support mirror mode");
|
||||
|
||||
argv = xmalloc((refspec_nr + 12) * sizeof(char *));
|
||||
argv[0] = "http-push";
|
||||
argc = 1;
|
||||
@@ -432,7 +439,7 @@ static int missing__target(int code, int result)
|
||||
|
||||
#define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
|
||||
|
||||
static struct ref *get_refs_via_curl(const struct transport *transport)
|
||||
static struct ref *get_refs_via_curl(struct transport *transport)
|
||||
{
|
||||
struct buffer buffer;
|
||||
char *data, *start, *mid;
|
||||
@@ -529,7 +536,7 @@ struct bundle_transport_data {
|
||||
struct bundle_header header;
|
||||
};
|
||||
|
||||
static struct ref *get_refs_from_bundle(const struct transport *transport)
|
||||
static struct ref *get_refs_from_bundle(struct transport *transport)
|
||||
{
|
||||
struct bundle_transport_data *data = transport->data;
|
||||
struct ref *result = NULL;
|
||||
@@ -601,7 +608,7 @@ static int set_git_option(struct transport *connection,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct ref *get_refs_via_connect(const struct transport *transport)
|
||||
static struct ref *get_refs_via_connect(struct transport *transport)
|
||||
{
|
||||
struct git_transport_data *data = transport->data;
|
||||
struct ref *refs;
|
||||
@@ -654,50 +661,17 @@ static int fetch_refs_via_pack(struct transport *transport,
|
||||
static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)
|
||||
{
|
||||
struct git_transport_data *data = transport->data;
|
||||
const char **argv;
|
||||
char *rem;
|
||||
int argc;
|
||||
int err;
|
||||
struct send_pack_args args;
|
||||
|
||||
argv = xmalloc((refspec_nr + 12) * sizeof(char *));
|
||||
argv[0] = "send-pack";
|
||||
argc = 1;
|
||||
if (flags & TRANSPORT_PUSH_ALL)
|
||||
argv[argc++] = "--all";
|
||||
if (flags & TRANSPORT_PUSH_FORCE)
|
||||
argv[argc++] = "--force";
|
||||
if (flags & TRANSPORT_PUSH_DRY_RUN)
|
||||
argv[argc++] = "--dry-run";
|
||||
if (flags & TRANSPORT_PUSH_VERBOSE)
|
||||
argv[argc++] = "--verbose";
|
||||
if (data->receivepack) {
|
||||
char *rp = xmalloc(strlen(data->receivepack) + 16);
|
||||
sprintf(rp, "--receive-pack=%s", data->receivepack);
|
||||
argv[argc++] = rp;
|
||||
}
|
||||
if (data->thin)
|
||||
argv[argc++] = "--thin";
|
||||
rem = xmalloc(strlen(transport->remote->name) + 10);
|
||||
sprintf(rem, "--remote=%s", transport->remote->name);
|
||||
argv[argc++] = rem;
|
||||
argv[argc++] = transport->url;
|
||||
while (refspec_nr--)
|
||||
argv[argc++] = *refspec++;
|
||||
argv[argc] = NULL;
|
||||
err = run_command_v_opt(argv, RUN_GIT_CMD);
|
||||
switch (err) {
|
||||
case -ERR_RUN_COMMAND_FORK:
|
||||
error("unable to fork for %s", argv[0]);
|
||||
case -ERR_RUN_COMMAND_EXEC:
|
||||
error("unable to exec %s", argv[0]);
|
||||
break;
|
||||
case -ERR_RUN_COMMAND_WAITPID:
|
||||
case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
|
||||
case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
|
||||
case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
|
||||
error("%s died with strange error", argv[0]);
|
||||
}
|
||||
return !!err;
|
||||
args.receivepack = data->receivepack;
|
||||
args.send_all = !!(flags & TRANSPORT_PUSH_ALL);
|
||||
args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);
|
||||
args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
|
||||
args.use_thin_pack = data->thin;
|
||||
args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE);
|
||||
args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
|
||||
|
||||
return send_pack(&args, transport->url, transport->remote, refspec_nr, refspec);
|
||||
}
|
||||
|
||||
static int disconnect_git(struct transport *transport)
|
||||
@@ -789,7 +763,7 @@ int transport_push(struct transport *transport,
|
||||
return transport->push(transport, refspec_nr, refspec, flags);
|
||||
}
|
||||
|
||||
struct ref *transport_get_remote_refs(struct transport *transport)
|
||||
const struct ref *transport_get_remote_refs(struct transport *transport)
|
||||
{
|
||||
if (!transport->remote_refs)
|
||||
transport->remote_refs = transport->get_refs_list(transport);
|
||||
|
||||
@@ -8,7 +8,7 @@ struct transport {
|
||||
struct remote *remote;
|
||||
const char *url;
|
||||
void *data;
|
||||
struct ref *remote_refs;
|
||||
const struct ref *remote_refs;
|
||||
|
||||
/**
|
||||
* Returns 0 if successful, positive if the option is not
|
||||
@@ -18,7 +18,7 @@ struct transport {
|
||||
int (*set_option)(struct transport *connection, const char *name,
|
||||
const char *value);
|
||||
|
||||
struct ref *(*get_refs_list)(const struct transport *transport);
|
||||
struct ref *(*get_refs_list)(struct transport *transport);
|
||||
int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs);
|
||||
int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags);
|
||||
|
||||
@@ -30,7 +30,8 @@ struct transport {
|
||||
#define TRANSPORT_PUSH_ALL 1
|
||||
#define TRANSPORT_PUSH_FORCE 2
|
||||
#define TRANSPORT_PUSH_DRY_RUN 4
|
||||
#define TRANSPORT_PUSH_VERBOSE 8
|
||||
#define TRANSPORT_PUSH_MIRROR 8
|
||||
#define TRANSPORT_PUSH_VERBOSE 16
|
||||
|
||||
/* Returns a transport suitable for the url */
|
||||
struct transport *transport_get(struct remote *, const char *);
|
||||
@@ -62,7 +63,7 @@ int transport_set_option(struct transport *transport, const char *name,
|
||||
int transport_push(struct transport *connection,
|
||||
int refspec_nr, const char **refspec, int flags);
|
||||
|
||||
struct ref *transport_get_remote_refs(struct transport *transport);
|
||||
const struct ref *transport_get_remote_refs(struct transport *transport);
|
||||
|
||||
int transport_fetch_refs(struct transport *transport, struct ref *refs);
|
||||
void transport_unlock_pack(struct transport *transport);
|
||||
|
||||
@@ -71,12 +71,8 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
|
||||
int remove;
|
||||
int baselen = strlen(base);
|
||||
int src_size = len + 1;
|
||||
int i_stk = i_stk;
|
||||
int retval = 0;
|
||||
|
||||
if (o->dir)
|
||||
i_stk = push_exclude_per_directory(o->dir, base, strlen(base));
|
||||
|
||||
do {
|
||||
int i;
|
||||
const char *first;
|
||||
@@ -255,8 +251,6 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
|
||||
} while (1);
|
||||
|
||||
leave_directory:
|
||||
if (o->dir)
|
||||
pop_exclude_per_directory(o->dir, i_stk);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user