mirror of
https://github.com/git/git.git
synced 2024-05-26 14:06:14 +02:00
Merge branch 'master' into js/merge
* master: (42 commits) git-svn: correctly handle packed-refs in refs/remotes/ add test case for recursive merge git-svn: correctly display fatal() error messages git-svn: allow dcommit to take an alternate head git-svn: enable logging of information not supported by git Clarify fetch error for missing objects. Move Fink and Ports check to after config file shortlog: fix segfault on empty authorname shortlog: remove "[PATCH]" prefix from shortlog output Make sure the empty tree exists when needed in merge-recursive. Don't use memcpy when source and dest. buffers may overlap no need to install manpages as executable Documentation: simpler shared repository creation shortlog: fix segfault on empty authorname Add branch.*.merge warning and documentation update Fix perl/ build. git-svn: use do_switch for --follow-parent if the SVN library supports it Fix documentation copy&paste typo git-svn: extra error check to ensure we open a file correctly Documentation: update git-clone man page with new behavior ...
This commit is contained in:
commit
8042ed1ceb
|
@ -56,8 +56,8 @@ man7: $(DOC_MAN7)
|
||||||
|
|
||||||
install: man
|
install: man
|
||||||
$(INSTALL) -d -m755 $(DESTDIR)$(man1dir) $(DESTDIR)$(man7dir)
|
$(INSTALL) -d -m755 $(DESTDIR)$(man1dir) $(DESTDIR)$(man7dir)
|
||||||
$(INSTALL) $(DOC_MAN1) $(DESTDIR)$(man1dir)
|
$(INSTALL) -m644 $(DOC_MAN1) $(DESTDIR)$(man1dir)
|
||||||
$(INSTALL) $(DOC_MAN7) $(DESTDIR)$(man7dir)
|
$(INSTALL) -m644 $(DOC_MAN7) $(DESTDIR)$(man7dir)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -125,10 +125,17 @@ apply.whitespace::
|
||||||
|
|
||||||
branch.<name>.remote::
|
branch.<name>.remote::
|
||||||
When in branch <name>, it tells `git fetch` which remote to fetch.
|
When in branch <name>, it tells `git fetch` which remote to fetch.
|
||||||
|
If this option is not given, `git fetch` defaults to remote "origin".
|
||||||
|
|
||||||
branch.<name>.merge::
|
branch.<name>.merge::
|
||||||
When in branch <name>, it tells `git fetch` the default remote branch
|
When in branch <name>, it tells `git fetch` the default refspec to
|
||||||
to be merged.
|
be marked for merging in FETCH_HEAD. The value has exactly to match
|
||||||
|
a remote part of one of the refspecs which are fetched from the remote
|
||||||
|
given by "branch.<name>.remote".
|
||||||
|
The merge information is used by `git pull` (which at first calls
|
||||||
|
`git fetch`) to lookup the default branch for merging. Without
|
||||||
|
this option, `git pull` defaults to merge the first refspec fetched.
|
||||||
|
Specify multiple values to get an octopus merge.
|
||||||
|
|
||||||
pager.color::
|
pager.color::
|
||||||
A boolean to enable/disable colored output when the pager is in
|
A boolean to enable/disable colored output when the pager is in
|
||||||
|
|
|
@ -1,43 +1,122 @@
|
||||||
git for CVS users
|
git for CVS users
|
||||||
=================
|
=================
|
||||||
|
|
||||||
So you're a CVS user. That's OK, it's a treatable condition. The job of
|
Git differs from CVS in that every working tree contains a repository with
|
||||||
this document is to put you on the road to recovery, by helping you
|
a full copy of the project history, and no repository is inherently more
|
||||||
convert an existing cvs repository to git, and by showing you how to use a
|
important than any other. However, you can emulate the CVS model by
|
||||||
git repository in a cvs-like fashion.
|
designating a single shared repository which people can synchronize with;
|
||||||
|
this document explains how to do that.
|
||||||
|
|
||||||
Some basic familiarity with git is required. This
|
Some basic familiarity with git is required. This
|
||||||
link:tutorial.html[tutorial introduction to git] should be sufficient.
|
link:tutorial.html[tutorial introduction to git] should be sufficient.
|
||||||
|
|
||||||
First, note some ways that git differs from CVS:
|
Developing against a shared repository
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
* Commits are atomic and project-wide, not per-file as in CVS.
|
Suppose a shared repository is set up in /pub/repo.git on the host
|
||||||
|
foo.com. Then as an individual committer you can clone the shared
|
||||||
|
repository over ssh with:
|
||||||
|
|
||||||
* Offline work is supported: you can make multiple commits locally,
|
------------------------------------------------
|
||||||
then submit them when you're ready.
|
$ git clone foo.com:/pub/repo.git/ my-project
|
||||||
|
$ cd my-project
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
* Branching is fast and easy.
|
and hack away. The equivalent of `cvs update` is
|
||||||
|
|
||||||
* Every working tree contains a repository with a full copy of the
|
------------------------------------------------
|
||||||
project history, and no repository is inherently more important than
|
$ git pull origin
|
||||||
any other. However, you can emulate the CVS model by designating a
|
------------------------------------------------
|
||||||
single shared repository which people can synchronize with; see below
|
|
||||||
for details.
|
which merges in any work that others might have done since the clone
|
||||||
|
operation. If there are uncommitted changes in your working tree, commit
|
||||||
|
them first before running git pull.
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
================================
|
||||||
|
The first `git clone` places the following in the
|
||||||
|
`my-project/.git/remotes/origin` file, and that's why the previous step
|
||||||
|
and the next step both work.
|
||||||
|
------------
|
||||||
|
URL: foo.com:/pub/project.git/
|
||||||
|
Pull: refs/heads/master:refs/remotes/origin/master
|
||||||
|
------------
|
||||||
|
================================
|
||||||
|
|
||||||
|
You can update the shared repository with your changes by first committing
|
||||||
|
your changes, and then using the gitlink:git-push[1] command:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ git push origin master
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
to "push" those commits to the shared repository. If someone else has
|
||||||
|
updated the repository more recently, `git push`, like `cvs commit`, will
|
||||||
|
complain, in which case you must pull any changes before attempting the
|
||||||
|
push again.
|
||||||
|
|
||||||
|
In the `git push` command above we specify the name of the remote branch
|
||||||
|
to update (`master`). If we leave that out, `git push` tries to update
|
||||||
|
any branches in the remote repository that have the same name as a branch
|
||||||
|
in the local repository. So the last `push` can be done with either of:
|
||||||
|
|
||||||
|
------------
|
||||||
|
$ git push origin
|
||||||
|
$ git push foo.com:/pub/project.git/
|
||||||
|
------------
|
||||||
|
|
||||||
|
as long as the shared repository does not have any branches
|
||||||
|
other than `master`.
|
||||||
|
|
||||||
|
Setting Up a Shared Repository
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
We assume you have already created a git repository for your project,
|
||||||
|
possibly created from scratch or from a tarball (see the
|
||||||
|
link:tutorial.html[tutorial]), or imported from an already existing CVS
|
||||||
|
repository (see the next section).
|
||||||
|
|
||||||
|
Assume your existing repo is at /home/alice/myproject. Create a new "bare"
|
||||||
|
repository (a repository without a working tree) and fetch your project into
|
||||||
|
it:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ mkdir /pub/my-repo.git
|
||||||
|
$ cd /pub/my-repo.git
|
||||||
|
$ git --bare init-db --shared
|
||||||
|
$ git --bare fetch /home/alice/myproject master:master
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
Next, give every team member read/write access to this repository. One
|
||||||
|
easy way to do this is to give all the team members ssh access to the
|
||||||
|
machine where the repository is hosted. If you don't want to give them a
|
||||||
|
full shell on the machine, there is a restricted shell which only allows
|
||||||
|
users to do git pushes and pulls; see gitlink:git-shell[1].
|
||||||
|
|
||||||
|
Put all the committers in the same group, and make the repository
|
||||||
|
writable by that group:
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
$ chgrp -R $group /pub/my-repo.git
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
Make sure committers have a umask of at most 027, so that the directories
|
||||||
|
they create are writable and searchable by other group members.
|
||||||
|
|
||||||
Importing a CVS archive
|
Importing a CVS archive
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
First, install version 2.1 or higher of cvsps from
|
First, install version 2.1 or higher of cvsps from
|
||||||
link:http://www.cobite.com/cvsps/[http://www.cobite.com/cvsps/] and make
|
link:http://www.cobite.com/cvsps/[http://www.cobite.com/cvsps/] and make
|
||||||
sure it is in your path. The magic command line is then
|
sure it is in your path. Then cd to a checked out CVS working directory
|
||||||
|
of the project you are interested in and run gitlink:git-cvsimport[1]:
|
||||||
|
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
$ git cvsimport -v -d <cvsroot> -C <destination> <module>
|
$ git cvsimport -C <destination>
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
|
|
||||||
This puts a git archive of the named CVS module in the directory
|
This puts a git archive of the named CVS module in the directory
|
||||||
<destination>, which will be created if necessary. The -v option makes
|
<destination>, which will be created if necessary.
|
||||||
the conversion script very chatty.
|
|
||||||
|
|
||||||
The import checks out from CVS every revision of every file. Reportedly
|
The import checks out from CVS every revision of every file. Reportedly
|
||||||
cvsimport can average some twenty revisions per second, so for a
|
cvsimport can average some twenty revisions per second, so for a
|
||||||
|
@ -55,14 +134,32 @@ work, you must not modify the imported branches; instead, create new
|
||||||
branches for your own changes, and merge in the imported branches as
|
branches for your own changes, and merge in the imported branches as
|
||||||
necessary.
|
necessary.
|
||||||
|
|
||||||
Development Models
|
Advanced Shared Repository Management
|
||||||
------------------
|
-------------------------------------
|
||||||
|
|
||||||
|
Git allows you to specify scripts called "hooks" to be run at certain
|
||||||
|
points. You can use these, for example, to send all commits to the shared
|
||||||
|
repository to a mailing list. See link:hooks.html[Hooks used by git].
|
||||||
|
|
||||||
|
You can enforce finer grained permissions using update hooks. See
|
||||||
|
link:howto/update-hook-example.txt[Controlling access to branches using
|
||||||
|
update hooks].
|
||||||
|
|
||||||
|
Providing CVS Access to a git Repository
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
It is also possible to provide true CVS access to a git repository, so
|
||||||
|
that developers can still use CVS; see gitlink:git-cvsserver[1] for
|
||||||
|
details.
|
||||||
|
|
||||||
|
Alternative Development Models
|
||||||
|
------------------------------
|
||||||
|
|
||||||
CVS users are accustomed to giving a group of developers commit access to
|
CVS users are accustomed to giving a group of developers commit access to
|
||||||
a common repository. In the next section we'll explain how to do this
|
a common repository. As we've seen, this is also possible with git.
|
||||||
with git. However, the distributed nature of git allows other development
|
However, the distributed nature of git allows other development models,
|
||||||
models, and you may want to first consider whether one of them might be a
|
and you may want to first consider whether one of them might be a better
|
||||||
better fit for your project.
|
fit for your project.
|
||||||
|
|
||||||
For example, you can choose a single person to maintain the project's
|
For example, you can choose a single person to maintain the project's
|
||||||
primary public repository. Other developers then clone this repository
|
primary public repository. Other developers then clone this repository
|
||||||
|
@ -75,230 +172,3 @@ variants of this model.
|
||||||
|
|
||||||
With a small group, developers may just pull changes from each other's
|
With a small group, developers may just pull changes from each other's
|
||||||
repositories without the need for a central maintainer.
|
repositories without the need for a central maintainer.
|
||||||
|
|
||||||
Emulating the CVS Development Model
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
Start with an ordinary git working directory containing the project, and
|
|
||||||
remove the checked-out files, keeping just the bare .git directory:
|
|
||||||
|
|
||||||
------------------------------------------------
|
|
||||||
$ mv project/.git /pub/repo.git
|
|
||||||
$ rm -r project/
|
|
||||||
------------------------------------------------
|
|
||||||
|
|
||||||
Next, give every team member read/write access to this repository. One
|
|
||||||
easy way to do this is to give all the team members ssh access to the
|
|
||||||
machine where the repository is hosted. If you don't want to give them a
|
|
||||||
full shell on the machine, there is a restricted shell which only allows
|
|
||||||
users to do git pushes and pulls; see gitlink:git-shell[1].
|
|
||||||
|
|
||||||
Put all the committers in the same group, and make the repository
|
|
||||||
writable by that group:
|
|
||||||
|
|
||||||
------------------------------------------------
|
|
||||||
$ chgrp -R $group repo.git
|
|
||||||
$ find repo.git -mindepth 1 -type d |xargs chmod ug+rwx,g+s
|
|
||||||
$ GIT_DIR=repo.git git repo-config core.sharedrepository true
|
|
||||||
------------------------------------------------
|
|
||||||
|
|
||||||
Make sure committers have a umask of at most 027, so that the directories
|
|
||||||
they create are writable and searchable by other group members.
|
|
||||||
|
|
||||||
Suppose this repository is now set up in /pub/repo.git on the host
|
|
||||||
foo.com. Then as an individual committer you can clone the shared
|
|
||||||
repository:
|
|
||||||
|
|
||||||
------------------------------------------------
|
|
||||||
$ git clone foo.com:/pub/repo.git/ my-project
|
|
||||||
$ cd my-project
|
|
||||||
------------------------------------------------
|
|
||||||
|
|
||||||
and hack away. The equivalent of `cvs update` is
|
|
||||||
|
|
||||||
------------------------------------------------
|
|
||||||
$ git pull origin
|
|
||||||
------------------------------------------------
|
|
||||||
|
|
||||||
which merges in any work that others might have done since the clone
|
|
||||||
operation.
|
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
================================
|
|
||||||
The first `git clone` places the following in the
|
|
||||||
`my-project/.git/remotes/origin` file, and that's why the previous step
|
|
||||||
and the next step both work.
|
|
||||||
------------
|
|
||||||
URL: foo.com:/pub/project.git/ my-project
|
|
||||||
Pull: master:origin
|
|
||||||
------------
|
|
||||||
================================
|
|
||||||
|
|
||||||
You can update the shared repository with your changes using:
|
|
||||||
|
|
||||||
------------------------------------------------
|
|
||||||
$ git push origin master
|
|
||||||
------------------------------------------------
|
|
||||||
|
|
||||||
If someone else has updated the repository more recently, `git push`, like
|
|
||||||
`cvs commit`, will complain, in which case you must pull any changes
|
|
||||||
before attempting the push again.
|
|
||||||
|
|
||||||
In the `git push` command above we specify the name of the remote branch
|
|
||||||
to update (`master`). If we leave that out, `git push` tries to update
|
|
||||||
any branches in the remote repository that have the same name as a branch
|
|
||||||
in the local repository. So the last `push` can be done with either of:
|
|
||||||
|
|
||||||
------------
|
|
||||||
$ git push origin
|
|
||||||
$ git push repo.shared.xz:/pub/scm/project.git/
|
|
||||||
------------
|
|
||||||
|
|
||||||
as long as the shared repository does not have any branches
|
|
||||||
other than `master`.
|
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
============
|
|
||||||
Because of this behavior, if the shared repository and the developer's
|
|
||||||
repository both have branches named `origin`, then a push like the above
|
|
||||||
attempts to update the `origin` branch in the shared repository from the
|
|
||||||
developer's `origin` branch. The results may be unexpected, so it's
|
|
||||||
usually best to remove any branch named `origin` from the shared
|
|
||||||
repository.
|
|
||||||
============
|
|
||||||
|
|
||||||
Advanced Shared Repository Management
|
|
||||||
-------------------------------------
|
|
||||||
|
|
||||||
Git allows you to specify scripts called "hooks" to be run at certain
|
|
||||||
points. You can use these, for example, to send all commits to the shared
|
|
||||||
repository to a mailing list. See link:hooks.html[Hooks used by git].
|
|
||||||
|
|
||||||
You can enforce finer grained permissions using update hooks. See
|
|
||||||
link:howto/update-hook-example.txt[Controlling access to branches using
|
|
||||||
update hooks].
|
|
||||||
|
|
||||||
CVS annotate
|
|
||||||
------------
|
|
||||||
|
|
||||||
So, something has gone wrong, and you don't know whom to blame, and
|
|
||||||
you're an ex-CVS user and used to do "cvs annotate" to see who caused
|
|
||||||
the breakage. You're looking for the "git annotate", and it's just
|
|
||||||
claiming not to find such a script. You're annoyed.
|
|
||||||
|
|
||||||
Yes, that's right. Core git doesn't do "annotate", although it's
|
|
||||||
technically possible, and there are at least two specialized scripts out
|
|
||||||
there that can be used to get equivalent information (see the git
|
|
||||||
mailing list archives for details).
|
|
||||||
|
|
||||||
git has a couple of alternatives, though, that you may find sufficient
|
|
||||||
or even superior depending on your use. One is called "git-whatchanged"
|
|
||||||
(for obvious reasons) and the other one is called "pickaxe" ("a tool for
|
|
||||||
the software archaeologist").
|
|
||||||
|
|
||||||
The "git-whatchanged" script is a truly trivial script that can give you
|
|
||||||
a good overview of what has changed in a file or a directory (or an
|
|
||||||
arbitrary list of files or directories). The "pickaxe" support is an
|
|
||||||
additional layer that can be used to further specify exactly what you're
|
|
||||||
looking for, if you already know the specific area that changed.
|
|
||||||
|
|
||||||
Let's step back a bit and think about the reason why you would
|
|
||||||
want to do "cvs annotate a-file.c" to begin with.
|
|
||||||
|
|
||||||
You would use "cvs annotate" on a file when you have trouble
|
|
||||||
with a function (or even a single "if" statement in a function)
|
|
||||||
that happens to be defined in the file, which does not do what
|
|
||||||
you want it to do. And you would want to find out why it was
|
|
||||||
written that way, because you are about to modify it to suit
|
|
||||||
your needs, and at the same time you do not want to break its
|
|
||||||
current callers. For that, you are trying to find out why the
|
|
||||||
original author did things that way in the original context.
|
|
||||||
|
|
||||||
Many times, it may be enough to see the commit log messages of
|
|
||||||
commits that touch the file in question, possibly along with the
|
|
||||||
patches themselves, like this:
|
|
||||||
|
|
||||||
$ git-whatchanged -p a-file.c
|
|
||||||
|
|
||||||
This will show log messages and patches for each commit that
|
|
||||||
touches a-file.
|
|
||||||
|
|
||||||
This, however, may not be very useful when this file has many
|
|
||||||
modifications that are not related to the piece of code you are
|
|
||||||
interested in. You would see many log messages and patches that
|
|
||||||
do not have anything to do with the piece of code you are
|
|
||||||
interested in. As an example, assuming that you have this piece
|
|
||||||
of code that you are interested in in the HEAD version:
|
|
||||||
|
|
||||||
if (frotz) {
|
|
||||||
nitfol();
|
|
||||||
}
|
|
||||||
|
|
||||||
you would use git-rev-list and git-diff-tree like this:
|
|
||||||
|
|
||||||
$ git-rev-list HEAD |
|
|
||||||
git-diff-tree --stdin -v -p -S'if (frotz) {
|
|
||||||
nitfol();
|
|
||||||
}'
|
|
||||||
|
|
||||||
We have already talked about the "\--stdin" form of git-diff-tree
|
|
||||||
command that reads the list of commits and compares each commit
|
|
||||||
with its parents (otherwise you should go back and read the tutorial).
|
|
||||||
The git-whatchanged command internally runs
|
|
||||||
the equivalent of the above command, and can be used like this:
|
|
||||||
|
|
||||||
$ git-whatchanged -p -S'if (frotz) {
|
|
||||||
nitfol();
|
|
||||||
}'
|
|
||||||
|
|
||||||
When the -S option is used, git-diff-tree command outputs
|
|
||||||
differences between two commits only if one tree has the
|
|
||||||
specified string in a file and the corresponding file in the
|
|
||||||
other tree does not. The above example looks for a commit that
|
|
||||||
has the "if" statement in it in a file, but its parent commit
|
|
||||||
does not have it in the same shape in the corresponding file (or
|
|
||||||
the other way around, where the parent has it and the commit
|
|
||||||
does not), and the differences between them are shown, along
|
|
||||||
with the commit message (thanks to the -v flag). It does not
|
|
||||||
show anything for commits that do not touch this "if" statement.
|
|
||||||
|
|
||||||
Also, in the original context, the same statement might have
|
|
||||||
appeared at first in a different file and later the file was
|
|
||||||
renamed to "a-file.c". CVS annotate would not help you to go
|
|
||||||
back across such a rename, but git would still help you in such
|
|
||||||
a situation. For that, you can give the -C flag to
|
|
||||||
git-diff-tree, like this:
|
|
||||||
|
|
||||||
$ git-whatchanged -p -C -S'if (frotz) {
|
|
||||||
nitfol();
|
|
||||||
}'
|
|
||||||
|
|
||||||
When the -C flag is used, file renames and copies are followed.
|
|
||||||
So if the "if" statement in question happens to be in "a-file.c"
|
|
||||||
in the current HEAD commit, even if the file was originally
|
|
||||||
called "o-file.c" and then renamed in an earlier commit, or if
|
|
||||||
the file was created by copying an existing "o-file.c" in an
|
|
||||||
earlier commit, you will not lose track. If the "if" statement
|
|
||||||
did not change across such a rename or copy, then the commit that
|
|
||||||
does rename or copy would not show in the output, and if the
|
|
||||||
"if" statement was modified while the file was still called
|
|
||||||
"o-file.c", it would find the commit that changed the statement
|
|
||||||
when it was in "o-file.c".
|
|
||||||
|
|
||||||
NOTE: The current version of "git-diff-tree -C" is not eager
|
|
||||||
enough to find copies, and it will miss the fact that a-file.c
|
|
||||||
was created by copying o-file.c unless o-file.c was somehow
|
|
||||||
changed in the same commit.
|
|
||||||
|
|
||||||
You can use the --pickaxe-all flag in addition to the -S flag.
|
|
||||||
This causes the differences from all the files contained in
|
|
||||||
those two commits, not just the differences between the files
|
|
||||||
that contain this changed "if" statement:
|
|
||||||
|
|
||||||
$ git-whatchanged -p -C -S'if (frotz) {
|
|
||||||
nitfol();
|
|
||||||
}' --pickaxe-all
|
|
||||||
|
|
||||||
NOTE: This option is called "--pickaxe-all" because -S
|
|
||||||
option is internally called "pickaxe", a tool for software
|
|
||||||
archaeologists.
|
|
||||||
|
|
|
@ -129,5 +129,21 @@
|
||||||
-a::
|
-a::
|
||||||
Shorthand for "--text".
|
Shorthand for "--text".
|
||||||
|
|
||||||
|
--ignore-space-change::
|
||||||
|
Ignore changes in amount of white space. This ignores white
|
||||||
|
space at line end, and consider all other sequences of one or
|
||||||
|
more white space characters to be equivalent.
|
||||||
|
|
||||||
|
-b::
|
||||||
|
Shorthand for "--ignore-space-change".
|
||||||
|
|
||||||
|
--ignore-all-space::
|
||||||
|
Ignore white space when comparing lines. This ignores
|
||||||
|
difference even if one line has white space where the other
|
||||||
|
line has none.
|
||||||
|
|
||||||
|
-w::
|
||||||
|
Shorthand for "--ignore-all-space".
|
||||||
|
|
||||||
For more detailed explanation on these common options, see also
|
For more detailed explanation on these common options, see also
|
||||||
link:diffcore.html[diffcore documentation].
|
link:diffcore.html[diffcore documentation].
|
||||||
|
|
|
@ -11,27 +11,26 @@ SYNOPSIS
|
||||||
[verse]
|
[verse]
|
||||||
'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
|
'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
|
||||||
[-o <name>] [-u <upload-pack>] [--reference <repository>]
|
[-o <name>] [-u <upload-pack>] [--reference <repository>]
|
||||||
[--use-separate-remote | --use-immingled-remote] <repository>
|
[--use-separate-remote | --no-separate-remote] <repository>
|
||||||
[<directory>]
|
[<directory>]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
Clones a repository into a newly created directory. All remote
|
|
||||||
branch heads are copied under `$GIT_DIR/refs/heads/`, except
|
|
||||||
that the remote `master` is also copied to `origin` branch.
|
|
||||||
|
|
||||||
In addition, `$GIT_DIR/remotes/origin` file is set up to have
|
Clones a repository into a newly created directory, creates
|
||||||
this line:
|
remote-tracking branches for each branch in the cloned repository
|
||||||
|
(visible using `git branch -r`), and creates and checks out a master
|
||||||
|
branch equal to the cloned repository's master branch.
|
||||||
|
|
||||||
Pull: master:origin
|
After the clone, a plain `git fetch` without arguments will update
|
||||||
|
all the remote-tracking branches, and a `git pull` without
|
||||||
This is to help the typical workflow of working off of the
|
arguments will in addition merge the remote master branch into the
|
||||||
remote `master` branch. Every time `git pull` without argument
|
current branch.
|
||||||
is run, the progress on the remote `master` branch is tracked by
|
|
||||||
copying it into the local `origin` branch, and merged into the
|
|
||||||
branch you are currently working on. Remote branches other than
|
|
||||||
`master` are also added there to be tracked.
|
|
||||||
|
|
||||||
|
This default configuration is achieved by creating references to
|
||||||
|
the remote branch heads under `$GIT_DIR/refs/remotes/origin` and
|
||||||
|
by initializing `remote.origin.url` and `remote.origin.fetch`
|
||||||
|
configuration variables.
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
-------
|
-------
|
||||||
|
@ -105,7 +104,7 @@ OPTIONS
|
||||||
of `$GIT_DIR/refs/heads/`. Only the local master branch is
|
of `$GIT_DIR/refs/heads/`. Only the local master branch is
|
||||||
saved in the latter. This is the default.
|
saved in the latter. This is the default.
|
||||||
|
|
||||||
--use-immingled-remote::
|
--no-separate-remote::
|
||||||
Save remotes heads in the same namespace as the local
|
Save remotes heads in the same namespace as the local
|
||||||
heads, `$GIT_DIR/refs/heads/'. In regular repositories,
|
heads, `$GIT_DIR/refs/heads/'. In regular repositories,
|
||||||
this is a legacy setup git-clone created by default in
|
this is a legacy setup git-clone created by default in
|
||||||
|
|
|
@ -57,11 +57,13 @@ See '<<fetch-args,Additional Fetch Arguments>>' if you are interested in
|
||||||
manually joining branches on commit.
|
manually joining branches on commit.
|
||||||
|
|
||||||
'dcommit'::
|
'dcommit'::
|
||||||
Commit all diffs from the current HEAD directly to the SVN
|
Commit all diffs from a specified head directly to the SVN
|
||||||
repository, and then rebase or reset (depending on whether or
|
repository, and then rebase or reset (depending on whether or
|
||||||
not there is a diff between SVN and HEAD). It is recommended
|
not there is a diff between SVN and head). It is recommended
|
||||||
that you run git-svn fetch and rebase (not pull) your commits
|
that you run git-svn fetch and rebase (not pull) your commits
|
||||||
against the latest changes in the SVN repository.
|
against the latest changes in the SVN repository.
|
||||||
|
An optional command-line argument may be specified as an
|
||||||
|
alternative to HEAD.
|
||||||
This is advantageous over 'commit' (below) because it produces
|
This is advantageous over 'commit' (below) because it produces
|
||||||
cleaner, more linear history.
|
cleaner, more linear history.
|
||||||
|
|
||||||
|
|
49
Makefile
49
Makefile
|
@ -91,6 +91,10 @@ all:
|
||||||
#
|
#
|
||||||
# Define USE_STDEV below if you want git to care about the underlying device
|
# 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-cache perspective.
|
||||||
|
#
|
||||||
|
# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
|
||||||
|
# MakeMaker (e.g. using ActiveState under Cygwin).
|
||||||
|
#
|
||||||
|
|
||||||
GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
|
GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
|
||||||
@$(SHELL_PATH) ./GIT-VERSION-GEN
|
@$(SHELL_PATH) ./GIT-VERSION-GEN
|
||||||
|
@ -323,18 +327,6 @@ ifeq ($(uname_S),Darwin)
|
||||||
NEEDS_SSL_WITH_CRYPTO = YesPlease
|
NEEDS_SSL_WITH_CRYPTO = YesPlease
|
||||||
NEEDS_LIBICONV = YesPlease
|
NEEDS_LIBICONV = YesPlease
|
||||||
NO_STRLCPY = YesPlease
|
NO_STRLCPY = YesPlease
|
||||||
ifndef NO_FINK
|
|
||||||
ifeq ($(shell test -d /sw/lib && echo y),y)
|
|
||||||
BASIC_CFLAGS += -I/sw/include
|
|
||||||
BASIC_LDFLAGS += -L/sw/lib
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
ifndef NO_DARWIN_PORTS
|
|
||||||
ifeq ($(shell test -d /opt/local/lib && echo y),y)
|
|
||||||
BASIC_CFLAGS += -I/opt/local/include
|
|
||||||
BASIC_LDFLAGS += -L/opt/local/lib
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
ifeq ($(uname_S),SunOS)
|
ifeq ($(uname_S),SunOS)
|
||||||
NEEDS_SOCKET = YesPlease
|
NEEDS_SOCKET = YesPlease
|
||||||
|
@ -412,6 +404,21 @@ endif
|
||||||
-include config.mak.autogen
|
-include config.mak.autogen
|
||||||
-include config.mak
|
-include config.mak
|
||||||
|
|
||||||
|
ifeq ($(uname_S),Darwin)
|
||||||
|
ifndef NO_FINK
|
||||||
|
ifeq ($(shell test -d /sw/lib && echo y),y)
|
||||||
|
BASIC_CFLAGS += -I/sw/include
|
||||||
|
BASIC_LDFLAGS += -L/sw/lib
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
ifndef NO_DARWIN_PORTS
|
||||||
|
ifeq ($(shell test -d /opt/local/lib && echo y),y)
|
||||||
|
BASIC_CFLAGS += -I/opt/local/include
|
||||||
|
BASIC_LDFLAGS += -L/opt/local/lib
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
ifndef NO_CURL
|
ifndef NO_CURL
|
||||||
ifdef CURLDIR
|
ifdef CURLDIR
|
||||||
# This is still problematic -- gcc does not always want -R.
|
# This is still problematic -- gcc does not always want -R.
|
||||||
|
@ -540,6 +547,9 @@ endif
|
||||||
ifdef NO_ACCURATE_DIFF
|
ifdef NO_ACCURATE_DIFF
|
||||||
BASIC_CFLAGS += -DNO_ACCURATE_DIFF
|
BASIC_CFLAGS += -DNO_ACCURATE_DIFF
|
||||||
endif
|
endif
|
||||||
|
ifdef NO_PERL_MAKEMAKER
|
||||||
|
export NO_PERL_MAKEMAKER
|
||||||
|
endif
|
||||||
|
|
||||||
# Shell quote (do not use $(call) to accommodate ancient setups);
|
# Shell quote (do not use $(call) to accommodate ancient setups);
|
||||||
|
|
||||||
|
@ -569,8 +579,8 @@ export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
|
||||||
|
|
||||||
all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi
|
all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi
|
||||||
|
|
||||||
all: perl/Makefile
|
all:
|
||||||
$(MAKE) -C perl
|
$(MAKE) -C perl PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
|
||||||
$(MAKE) -C templates
|
$(MAKE) -C templates
|
||||||
|
|
||||||
strip: $(PROGRAMS) git$X
|
strip: $(PROGRAMS) git$X
|
||||||
|
@ -603,7 +613,11 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
|
||||||
chmod +x $@+
|
chmod +x $@+
|
||||||
mv $@+ $@
|
mv $@+ $@
|
||||||
|
|
||||||
$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/Makefile
|
$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
|
||||||
|
|
||||||
|
perl/perl.mak: GIT-CFLAGS
|
||||||
|
$(MAKE) -C perl PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
|
||||||
|
|
||||||
$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
|
$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
|
||||||
rm -f $@ $@+
|
rm -f $@ $@+
|
||||||
INSTLIBDIR=`$(MAKE) -C perl -s --no-print-directory instlibdir` && \
|
INSTLIBDIR=`$(MAKE) -C perl -s --no-print-directory instlibdir` && \
|
||||||
|
@ -798,7 +812,7 @@ install: all
|
||||||
$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
|
$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
|
||||||
$(INSTALL) git$X gitk '$(DESTDIR_SQ)$(bindir_SQ)'
|
$(INSTALL) git$X gitk '$(DESTDIR_SQ)$(bindir_SQ)'
|
||||||
$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
|
$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
|
||||||
$(MAKE) -C perl install
|
$(MAKE) -C perl prefix='$(prefix_SQ)' install
|
||||||
if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \
|
if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \
|
||||||
then \
|
then \
|
||||||
ln -f '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \
|
ln -f '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \
|
||||||
|
@ -868,8 +882,7 @@ clean:
|
||||||
rm -f $(htmldocs).tar.gz $(manpages).tar.gz
|
rm -f $(htmldocs).tar.gz $(manpages).tar.gz
|
||||||
rm -f gitweb/gitweb.cgi
|
rm -f gitweb/gitweb.cgi
|
||||||
$(MAKE) -C Documentation/ clean
|
$(MAKE) -C Documentation/ clean
|
||||||
[ ! -f perl/Makefile ] || $(MAKE) -C perl/ clean || $(MAKE) -C perl/ clean
|
$(MAKE) -C perl clean
|
||||||
rm -f perl/ppport.h perl/Makefile.old
|
|
||||||
$(MAKE) -C templates/ clean
|
$(MAKE) -C templates/ clean
|
||||||
$(MAKE) -C t/ clean
|
$(MAKE) -C t/ clean
|
||||||
rm -f GIT-VERSION-FILE GIT-CFLAGS
|
rm -f GIT-VERSION-FILE GIT-CFLAGS
|
||||||
|
|
11
builtin-mv.c
11
builtin-mv.c
|
@ -146,21 +146,24 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
||||||
&& lstat(dst, &st) == 0)
|
&& lstat(dst, &st) == 0)
|
||||||
bad = "cannot move directory over file";
|
bad = "cannot move directory over file";
|
||||||
else if (src_is_dir) {
|
else if (src_is_dir) {
|
||||||
|
const char *src_w_slash = add_slash(src);
|
||||||
|
int len_w_slash = length + 1;
|
||||||
int first, last;
|
int first, last;
|
||||||
|
|
||||||
modes[i] = WORKING_DIRECTORY;
|
modes[i] = WORKING_DIRECTORY;
|
||||||
|
|
||||||
first = cache_name_pos(src, length);
|
first = cache_name_pos(src_w_slash, len_w_slash);
|
||||||
if (first >= 0)
|
if (first >= 0)
|
||||||
die ("Huh? %s/ is in index?", src);
|
die ("Huh? %.*s is in index?",
|
||||||
|
len_w_slash, src_w_slash);
|
||||||
|
|
||||||
first = -1 - first;
|
first = -1 - first;
|
||||||
for (last = first; last < active_nr; last++) {
|
for (last = first; last < active_nr; last++) {
|
||||||
const char *path = active_cache[last]->name;
|
const char *path = active_cache[last]->name;
|
||||||
if (strncmp(path, src, length)
|
if (strncmp(path, src_w_slash, len_w_slash))
|
||||||
|| path[length] != '/')
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
free((char *)src_w_slash);
|
||||||
|
|
||||||
if (last - first < 1)
|
if (last - first < 1)
|
||||||
bad = "source directory is empty";
|
bad = "source directory is empty";
|
||||||
|
|
|
@ -188,18 +188,25 @@ static void read_from_stdin(struct path_list *list)
|
||||||
bob = buffer + strlen(buffer);
|
bob = buffer + strlen(buffer);
|
||||||
else {
|
else {
|
||||||
offset = 8;
|
offset = 8;
|
||||||
while (isspace(bob[-1]))
|
while (buffer + offset < bob &&
|
||||||
|
isspace(bob[-1]))
|
||||||
bob--;
|
bob--;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (fgets(buffer2, sizeof(buffer2), stdin) &&
|
while (fgets(buffer2, sizeof(buffer2), stdin) &&
|
||||||
buffer2[0] != '\n')
|
buffer2[0] != '\n')
|
||||||
; /* chomp input */
|
; /* chomp input */
|
||||||
if (fgets(buffer2, sizeof(buffer2), stdin))
|
if (fgets(buffer2, sizeof(buffer2), stdin)) {
|
||||||
|
int l2 = strlen(buffer2);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < l2; i++)
|
||||||
|
if (!isspace(buffer2[i]))
|
||||||
|
break;
|
||||||
insert_author_oneline(list,
|
insert_author_oneline(list,
|
||||||
buffer + offset,
|
buffer + offset,
|
||||||
bob - buffer - offset,
|
bob - buffer - offset,
|
||||||
buffer2, strlen(buffer2));
|
buffer2 + i, l2 - i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,7 +243,7 @@ static void get_from_rev(struct rev_info *rev, struct path_list *list)
|
||||||
author = scratch;
|
author = scratch;
|
||||||
authorlen = strlen(scratch);
|
authorlen = strlen(scratch);
|
||||||
} else {
|
} else {
|
||||||
while (bracket[-1] == ' ')
|
if (bracket[-1] == ' ')
|
||||||
bracket--;
|
bracket--;
|
||||||
|
|
||||||
author = buffer + 7;
|
author = buffer + 7;
|
||||||
|
|
15
fetch.c
15
fetch.c
|
@ -22,14 +22,15 @@ void pull_say(const char *fmt, const char *hex)
|
||||||
fprintf(stderr, fmt, hex);
|
fprintf(stderr, fmt, hex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void report_missing(const char *what, const unsigned char *missing)
|
static void report_missing(const struct object *obj)
|
||||||
{
|
{
|
||||||
char missing_hex[41];
|
char missing_hex[41];
|
||||||
|
strcpy(missing_hex, sha1_to_hex(obj->sha1));;
|
||||||
strcpy(missing_hex, sha1_to_hex(missing));;
|
fprintf(stderr, "Cannot obtain needed %s %s\n",
|
||||||
fprintf(stderr,
|
obj->type ? typename(obj->type): "object", missing_hex);
|
||||||
"Cannot obtain needed %s %s\nwhile processing commit %s.\n",
|
if (!is_null_sha1(current_commit_sha1))
|
||||||
what, missing_hex, sha1_to_hex(current_commit_sha1));
|
fprintf(stderr, "while processing commit %s.\n",
|
||||||
|
sha1_to_hex(current_commit_sha1));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int process(struct object *obj);
|
static int process(struct object *obj);
|
||||||
|
@ -177,7 +178,7 @@ static int loop(void)
|
||||||
*/
|
*/
|
||||||
if (! (obj->flags & TO_SCAN)) {
|
if (! (obj->flags & TO_SCAN)) {
|
||||||
if (fetch(obj->sha1)) {
|
if (fetch(obj->sha1)) {
|
||||||
report_missing(typename(obj->type), obj->sha1);
|
report_missing(obj);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
git-clone.sh
16
git-clone.sh
|
@ -14,7 +14,7 @@ die() {
|
||||||
}
|
}
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
die "Usage: $0 [--template=<template_directory>] [--use-immingled-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
|
die "Usage: $0 [--template=<template_directory>] [--no-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
|
||||||
}
|
}
|
||||||
|
|
||||||
get_repo_base() {
|
get_repo_base() {
|
||||||
|
@ -140,7 +140,7 @@ while
|
||||||
*,--use-separate-remote)
|
*,--use-separate-remote)
|
||||||
# default
|
# default
|
||||||
use_separate_remote=t ;;
|
use_separate_remote=t ;;
|
||||||
*,--use-immingled-remote)
|
*,--no-separate-remote)
|
||||||
use_separate_remote= ;;
|
use_separate_remote= ;;
|
||||||
1,--reference) usage ;;
|
1,--reference) usage ;;
|
||||||
*,--reference)
|
*,--reference)
|
||||||
|
@ -176,7 +176,7 @@ repo="$1"
|
||||||
test -n "$repo" ||
|
test -n "$repo" ||
|
||||||
die 'you must specify a repository to clone.'
|
die 'you must specify a repository to clone.'
|
||||||
|
|
||||||
# --bare implies --no-checkout and --use-immingled-remote
|
# --bare implies --no-checkout and --no-separate-remote
|
||||||
if test yes = "$bare"
|
if test yes = "$bare"
|
||||||
then
|
then
|
||||||
if test yes = "$origin_override"
|
if test yes = "$origin_override"
|
||||||
|
@ -377,9 +377,9 @@ then
|
||||||
*) origin_track="$remote_top/$origin"
|
*) origin_track="$remote_top/$origin"
|
||||||
git-update-ref "refs/heads/$origin" "$head_sha1" ;;
|
git-update-ref "refs/heads/$origin" "$head_sha1" ;;
|
||||||
esac &&
|
esac &&
|
||||||
echo >"$GIT_DIR/remotes/$origin" \
|
git-repo-config remote."$origin".url "$repo" &&
|
||||||
"URL: $repo
|
git-repo-config remote."$origin".fetch \
|
||||||
Pull: refs/heads/$head_points_at:$origin_track" &&
|
"refs/heads/$head_points_at:$origin_track" &&
|
||||||
(cd "$GIT_DIR/$remote_top" && find . -type f -print) |
|
(cd "$GIT_DIR/$remote_top" && find . -type f -print) |
|
||||||
while read dotslref
|
while read dotslref
|
||||||
do
|
do
|
||||||
|
@ -393,8 +393,8 @@ Pull: refs/heads/$head_points_at:$origin_track" &&
|
||||||
then
|
then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
echo "Pull: refs/heads/${name}:$remote_top/${name}"
|
git-repo-config remote."$origin".fetch "refs/heads/${name}:$remote_top/${name}" '^$'
|
||||||
done >>"$GIT_DIR/remotes/$origin" &&
|
done &&
|
||||||
case "$use_separate_remote" in
|
case "$use_separate_remote" in
|
||||||
t)
|
t)
|
||||||
rm -f "refs/remotes/$origin/HEAD"
|
rm -f "refs/remotes/$origin/HEAD"
|
||||||
|
|
|
@ -116,6 +116,7 @@
|
||||||
close MSG;
|
close MSG;
|
||||||
|
|
||||||
my (@afiles, @dfiles, @mfiles, @dirs);
|
my (@afiles, @dfiles, @mfiles, @dirs);
|
||||||
|
my %amodes;
|
||||||
my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
|
my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
|
||||||
#print @files;
|
#print @files;
|
||||||
$? && die "Error in git-diff-tree";
|
$? && die "Error in git-diff-tree";
|
||||||
|
@ -124,6 +125,7 @@
|
||||||
my @fields = split(m!\s+!, $f);
|
my @fields = split(m!\s+!, $f);
|
||||||
if ($fields[4] eq 'A') {
|
if ($fields[4] eq 'A') {
|
||||||
my $path = $fields[5];
|
my $path = $fields[5];
|
||||||
|
$amodes{$path} = $fields[1];
|
||||||
push @afiles, $path;
|
push @afiles, $path;
|
||||||
# add any needed parent directories
|
# add any needed parent directories
|
||||||
$path = dirname $path;
|
$path = dirname $path;
|
||||||
|
@ -268,6 +270,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach my $f (@afiles) {
|
foreach my $f (@afiles) {
|
||||||
|
set_new_file_permissions($f, $amodes{$f});
|
||||||
if (grep { $_ eq $f } @bfiles) {
|
if (grep { $_ eq $f } @bfiles) {
|
||||||
system('cvs', 'add','-kb',$f);
|
system('cvs', 'add','-kb',$f);
|
||||||
} else {
|
} else {
|
||||||
|
@ -342,3 +345,13 @@ sub safe_pipe_capture {
|
||||||
}
|
}
|
||||||
return wantarray ? @output : join('',@output);
|
return wantarray ? @output : join('',@output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# For any file we want to add to cvs, we must first set its permissions
|
||||||
|
# properly, *before* the "cvs add ..." command. Otherwise, it is impossible
|
||||||
|
# to change the permission of the file in the CVS repository using only cvs
|
||||||
|
# commands. This should be fixed in cvs-1.12.14.
|
||||||
|
sub set_new_file_permissions {
|
||||||
|
my ($file, $perm) = @_;
|
||||||
|
chmod oct($perm), $file
|
||||||
|
or die "failed to set permissions of \"$file\": $!\n";
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
use bytes;
|
||||||
|
|
||||||
use Fcntl;
|
use Fcntl;
|
||||||
use File::Temp qw/tempdir tempfile/;
|
use File::Temp qw/tempdir tempfile/;
|
||||||
|
|
|
@ -188,8 +188,9 @@ else
|
||||||
# in this loop.
|
# in this loop.
|
||||||
merge_name=$(for remote
|
merge_name=$(for remote
|
||||||
do
|
do
|
||||||
rh=$(git-rev-parse --verify "$remote"^0 2>/dev/null) &&
|
rh=$(git-rev-parse --verify "$remote"^0 2>/dev/null) ||
|
||||||
bh=$(git show-ref -s --verify "refs/heads/$remote") &&
|
continue ;# not something we can merge
|
||||||
|
bh=$(git show-ref -s --verify "refs/heads/$remote" 2>/dev/null)
|
||||||
if test "$rh" = "$bh"
|
if test "$rh" = "$bh"
|
||||||
then
|
then
|
||||||
echo "$rh branch '$remote' of ."
|
echo "$rh branch '$remote' of ."
|
||||||
|
|
|
@ -116,7 +116,7 @@ expand_refs_wildcard () {
|
||||||
while read sha1 name
|
while read sha1 name
|
||||||
do
|
do
|
||||||
mapped=${name#"$from"}
|
mapped=${name#"$from"}
|
||||||
if test "z$name" != "z${name#'^{}'}" ||
|
if test "z$name" != "z${name%'^{}'}" ||
|
||||||
test "z$name" = "z$mapped"
|
test "z$name" = "z$mapped"
|
||||||
then
|
then
|
||||||
continue
|
continue
|
||||||
|
@ -134,6 +134,8 @@ canon_refs_list_for_fetch () {
|
||||||
# or the first one otherwise; add prefix . to the rest
|
# or the first one otherwise; add prefix . to the rest
|
||||||
# to prevent the secondary branches to be merged by default.
|
# to prevent the secondary branches to be merged by default.
|
||||||
merge_branches=
|
merge_branches=
|
||||||
|
found_mergeref=
|
||||||
|
curr_branch=
|
||||||
if test "$1" = "-d"
|
if test "$1" = "-d"
|
||||||
then
|
then
|
||||||
shift ; remote="$1" ; shift
|
shift ; remote="$1" ; shift
|
||||||
|
@ -171,6 +173,10 @@ canon_refs_list_for_fetch () {
|
||||||
dot_prefix= && break
|
dot_prefix= && break
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
if test -z $dot_prefix
|
||||||
|
then
|
||||||
|
found_mergeref=true
|
||||||
|
fi
|
||||||
case "$remote" in
|
case "$remote" in
|
||||||
'') remote=HEAD ;;
|
'') remote=HEAD ;;
|
||||||
refs/heads/* | refs/tags/* | refs/remotes/*) ;;
|
refs/heads/* | refs/tags/* | refs/remotes/*) ;;
|
||||||
|
@ -191,6 +197,11 @@ canon_refs_list_for_fetch () {
|
||||||
fi
|
fi
|
||||||
echo "${dot_prefix}${force}${remote}:${local}"
|
echo "${dot_prefix}${force}${remote}:${local}"
|
||||||
done
|
done
|
||||||
|
if test -z "$found_mergeref" -a "$curr_branch"
|
||||||
|
then
|
||||||
|
echo >&2 "Warning: No merge candidate found because value of config option
|
||||||
|
\"branch.${curr_branch}.merge\" does not match any remote branch fetched."
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Returns list of src: (no store), or src:dst (store)
|
# Returns list of src: (no store), or src:dst (store)
|
||||||
|
|
|
@ -30,4 +30,4 @@ echo " $url"
|
||||||
echo
|
echo
|
||||||
|
|
||||||
git log $baserev..$headrev | git-shortlog ;
|
git log $baserev..$headrev | git-shortlog ;
|
||||||
git diff --stat --summary $baserev..$headrev
|
git diff -M --stat --summary $baserev..$headrev
|
||||||
|
|
|
@ -63,6 +63,7 @@ case "$reset_type" in
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" "$GIT_DIR/SQUASH_MSG"
|
rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" \
|
||||||
|
"$GIT_DIR/SQUASH_MSG" "$GIT_DIR/MERGE_MSG"
|
||||||
|
|
||||||
exit $update_ref_status
|
exit $update_ref_status
|
||||||
|
|
275
git-svn.perl
275
git-svn.perl
|
@ -21,7 +21,17 @@
|
||||||
$ENV{LC_ALL} = 'C';
|
$ENV{LC_ALL} = 'C';
|
||||||
$| = 1; # unbuffer STDOUT
|
$| = 1; # unbuffer STDOUT
|
||||||
|
|
||||||
sub fatal (@) { print STDERR $@; exit 1 }
|
# properties that we do not log:
|
||||||
|
my %SKIP = ( 'svn:wc:ra_dav:version-url' => 1,
|
||||||
|
'svn:special' => 1,
|
||||||
|
'svn:executable' => 1,
|
||||||
|
'svn:entry:committed-rev' => 1,
|
||||||
|
'svn:entry:last-author' => 1,
|
||||||
|
'svn:entry:uuid' => 1,
|
||||||
|
'svn:entry:committed-date' => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
sub fatal (@) { print STDERR @_; exit 1 }
|
||||||
# If SVN:: library support is added, please make the dependencies
|
# If SVN:: library support is added, please make the dependencies
|
||||||
# optional and preserve the capability to use the command-line client.
|
# optional and preserve the capability to use the command-line client.
|
||||||
# use eval { require SVN::... } to make it lazy load
|
# use eval { require SVN::... } to make it lazy load
|
||||||
|
@ -72,7 +82,7 @@ sub nag_lib {
|
||||||
$_username, $_config_dir, $_no_auth_cache, $_xfer_delta,
|
$_username, $_config_dir, $_no_auth_cache, $_xfer_delta,
|
||||||
$_pager, $_color);
|
$_pager, $_color);
|
||||||
my (@_branch_from, %tree_map, %users, %rusers, %equiv);
|
my (@_branch_from, %tree_map, %users, %rusers, %equiv);
|
||||||
my ($_svn_co_url_revs, $_svn_pg_peg_revs);
|
my ($_svn_co_url_revs, $_svn_pg_peg_revs, $_svn_can_do_switch);
|
||||||
my @repo_path_split_cache;
|
my @repo_path_split_cache;
|
||||||
|
|
||||||
my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
|
my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
|
||||||
|
@ -459,6 +469,7 @@ sub fetch_lib {
|
||||||
$min = $max + 1;
|
$min = $max + 1;
|
||||||
$max += $inc;
|
$max += $inc;
|
||||||
$max = $head if ($max > $head);
|
$max = $head if ($max > $head);
|
||||||
|
$SVN = libsvn_connect($SVN_URL);
|
||||||
}
|
}
|
||||||
restore_index($index);
|
restore_index($index);
|
||||||
return { revision => $last_rev, commit => $last_commit };
|
return { revision => $last_rev, commit => $last_commit };
|
||||||
|
@ -593,8 +604,9 @@ sub commit_lib {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub dcommit {
|
sub dcommit {
|
||||||
|
my $head = shift || 'HEAD';
|
||||||
my $gs = "refs/remotes/$GIT_SVN";
|
my $gs = "refs/remotes/$GIT_SVN";
|
||||||
chomp(my @refs = safe_qx(qw/git-rev-list --no-merges/, "$gs..HEAD"));
|
chomp(my @refs = safe_qx(qw/git-rev-list --no-merges/, "$gs..$head"));
|
||||||
my $last_rev;
|
my $last_rev;
|
||||||
foreach my $d (reverse @refs) {
|
foreach my $d (reverse @refs) {
|
||||||
if (quiet_run('git-rev-parse','--verify',"$d~1") != 0) {
|
if (quiet_run('git-rev-parse','--verify',"$d~1") != 0) {
|
||||||
|
@ -621,16 +633,16 @@ sub dcommit {
|
||||||
}
|
}
|
||||||
return if $_dry_run;
|
return if $_dry_run;
|
||||||
fetch();
|
fetch();
|
||||||
my @diff = safe_qx(qw/git-diff-tree HEAD/, $gs);
|
my @diff = safe_qx('git-diff-tree', $head, $gs);
|
||||||
my @finish;
|
my @finish;
|
||||||
if (@diff) {
|
if (@diff) {
|
||||||
@finish = qw/rebase/;
|
@finish = qw/rebase/;
|
||||||
push @finish, qw/--merge/ if $_merge;
|
push @finish, qw/--merge/ if $_merge;
|
||||||
push @finish, "--strategy=$_strategy" if $_strategy;
|
push @finish, "--strategy=$_strategy" if $_strategy;
|
||||||
print STDERR "W: HEAD and $gs differ, using @finish:\n", @diff;
|
print STDERR "W: $head and $gs differ, using @finish:\n", @diff;
|
||||||
} else {
|
} else {
|
||||||
print "No changes between current HEAD and $gs\n",
|
print "No changes between current $head and $gs\n",
|
||||||
"Hard resetting to the latest $gs\n";
|
"Resetting to the latest $gs\n";
|
||||||
@finish = qw/reset --mixed/;
|
@finish = qw/reset --mixed/;
|
||||||
}
|
}
|
||||||
sys('git', @finish, $gs);
|
sys('git', @finish, $gs);
|
||||||
|
@ -2015,9 +2027,17 @@ sub git_commit {
|
||||||
|
|
||||||
# just in case we clobber the existing ref, we still want that ref
|
# just in case we clobber the existing ref, we still want that ref
|
||||||
# as our parent:
|
# as our parent:
|
||||||
if (my $cur = eval { file_to_s("$GIT_DIR/refs/remotes/$GIT_SVN") }) {
|
open my $null, '>', '/dev/null' or croak $!;
|
||||||
|
open my $stderr, '>&', \*STDERR or croak $!;
|
||||||
|
open STDERR, '>&', $null or croak $!;
|
||||||
|
if (my $cur = eval { safe_qx('git-rev-parse',
|
||||||
|
"refs/remotes/$GIT_SVN^0") }) {
|
||||||
|
chomp $cur;
|
||||||
push @tmp_parents, $cur;
|
push @tmp_parents, $cur;
|
||||||
}
|
}
|
||||||
|
open STDERR, '>&', $stderr or croak $!;
|
||||||
|
close $stderr or croak $!;
|
||||||
|
close $null or croak $!;
|
||||||
|
|
||||||
if (exists $tree_map{$tree}) {
|
if (exists $tree_map{$tree}) {
|
||||||
foreach my $p (@{$tree_map{$tree}}) {
|
foreach my $p (@{$tree_map{$tree}}) {
|
||||||
|
@ -2876,6 +2896,24 @@ sub libsvn_connect {
|
||||||
return $ra;
|
return $ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub libsvn_can_do_switch {
|
||||||
|
unless (defined $_svn_can_do_switch) {
|
||||||
|
my $pool = SVN::Pool->new;
|
||||||
|
my $rep = eval {
|
||||||
|
$SVN->do_switch(1, '', 0, $SVN->{url},
|
||||||
|
SVN::Delta::Editor->new, $pool);
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
$_svn_can_do_switch = 0;
|
||||||
|
} else {
|
||||||
|
$rep->abort_report($pool);
|
||||||
|
$_svn_can_do_switch = 1;
|
||||||
|
}
|
||||||
|
$pool->clear;
|
||||||
|
}
|
||||||
|
$_svn_can_do_switch;
|
||||||
|
}
|
||||||
|
|
||||||
sub libsvn_dup_ra {
|
sub libsvn_dup_ra {
|
||||||
my ($ra) = @_;
|
my ($ra) = @_;
|
||||||
SVN::Ra->new(map { $_ => $ra->{$_} } qw/config url
|
SVN::Ra->new(map { $_ => $ra->{$_} } qw/config url
|
||||||
|
@ -2883,7 +2921,7 @@ sub libsvn_dup_ra {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub libsvn_get_file {
|
sub libsvn_get_file {
|
||||||
my ($gui, $f, $rev, $chg) = @_;
|
my ($gui, $f, $rev, $chg, $untracked) = @_;
|
||||||
$f =~ s#^/##;
|
$f =~ s#^/##;
|
||||||
print "\t$chg\t$f\n" unless $_q;
|
print "\t$chg\t$f\n" unless $_q;
|
||||||
|
|
||||||
|
@ -2921,11 +2959,25 @@ sub libsvn_get_file {
|
||||||
waitpid $pid, 0;
|
waitpid $pid, 0;
|
||||||
$hash =~ /^$sha1$/o or die "not a sha1: $hash\n";
|
$hash =~ /^$sha1$/o or die "not a sha1: $hash\n";
|
||||||
}
|
}
|
||||||
|
%{$untracked->{file_prop}->{$f}} = %$props;
|
||||||
print $gui $mode,' ',$hash,"\t",$f,"\0" or croak $!;
|
print $gui $mode,' ',$hash,"\t",$f,"\0" or croak $!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub uri_encode {
|
||||||
|
my ($f) = @_;
|
||||||
|
$f =~ s#([^a-zA-Z0-9\*!\:_\./\-])#uc sprintf("%%%02x",ord($1))#eg;
|
||||||
|
$f
|
||||||
|
}
|
||||||
|
|
||||||
|
sub uri_decode {
|
||||||
|
my ($f) = @_;
|
||||||
|
$f =~ tr/+/ /;
|
||||||
|
$f =~ s/%([A-F0-9]{2})/chr hex($1)/ge;
|
||||||
|
$f
|
||||||
|
}
|
||||||
|
|
||||||
sub libsvn_log_entry {
|
sub libsvn_log_entry {
|
||||||
my ($rev, $author, $date, $msg, $parents) = @_;
|
my ($rev, $author, $date, $msg, $parents, $untracked) = @_;
|
||||||
my ($Y,$m,$d,$H,$M,$S) = ($date =~ /^(\d{4})\-(\d\d)\-(\d\d)T
|
my ($Y,$m,$d,$H,$M,$S) = ($date =~ /^(\d{4})\-(\d\d)\-(\d\d)T
|
||||||
(\d\d)\:(\d\d)\:(\d\d).\d+Z$/x)
|
(\d\d)\:(\d\d)\:(\d\d).\d+Z$/x)
|
||||||
or die "Unable to parse date: $date\n";
|
or die "Unable to parse date: $date\n";
|
||||||
|
@ -2933,8 +2985,65 @@ sub libsvn_log_entry {
|
||||||
die "Author: $author not defined in $_authors file\n";
|
die "Author: $author not defined in $_authors file\n";
|
||||||
}
|
}
|
||||||
$msg = '' if ($rev == 0 && !defined $msg);
|
$msg = '' if ($rev == 0 && !defined $msg);
|
||||||
return { revision => $rev, date => "+0000 $Y-$m-$d $H:$M:$S",
|
|
||||||
author => $author, msg => $msg."\n", parents => $parents || [] }
|
open my $un, '>>', "$GIT_SVN_DIR/unhandled.log" or croak $!;
|
||||||
|
my $h;
|
||||||
|
print $un "r$rev\n" or croak $!;
|
||||||
|
$h = $untracked->{empty};
|
||||||
|
foreach (sort keys %$h) {
|
||||||
|
my $act = $h->{$_} ? '+empty_dir' : '-empty_dir';
|
||||||
|
print $un " $act: ", uri_encode($_), "\n" or croak $!;
|
||||||
|
warn "W: $act: $_\n";
|
||||||
|
}
|
||||||
|
foreach my $t (qw/dir_prop file_prop/) {
|
||||||
|
$h = $untracked->{$t} or next;
|
||||||
|
foreach my $path (sort keys %$h) {
|
||||||
|
my $ppath = $path eq '' ? '.' : $path;
|
||||||
|
foreach my $prop (sort keys %{$h->{$path}}) {
|
||||||
|
next if $SKIP{$prop};
|
||||||
|
my $v = $h->{$path}->{$prop};
|
||||||
|
if (defined $v) {
|
||||||
|
print $un " +$t: ",
|
||||||
|
uri_encode($ppath), ' ',
|
||||||
|
uri_encode($prop), ' ',
|
||||||
|
uri_encode($v), "\n"
|
||||||
|
or croak $!;
|
||||||
|
} else {
|
||||||
|
print $un " -$t: ",
|
||||||
|
uri_encode($ppath), ' ',
|
||||||
|
uri_encode($prop), "\n"
|
||||||
|
or croak $!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach my $t (qw/absent_file absent_directory/) {
|
||||||
|
$h = $untracked->{$t} or next;
|
||||||
|
foreach my $parent (sort keys %$h) {
|
||||||
|
foreach my $path (sort @{$h->{$parent}}) {
|
||||||
|
print $un " $t: ",
|
||||||
|
uri_encode("$parent/$path"), "\n"
|
||||||
|
or croak $!;
|
||||||
|
warn "W: $t: $parent/$path ",
|
||||||
|
"Insufficient permissions?\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# revprops (make this optional? it's an extra network trip...)
|
||||||
|
my $pool = SVN::Pool->new;
|
||||||
|
my $rp = $SVN->rev_proplist($rev, $pool);
|
||||||
|
foreach (sort keys %$rp) {
|
||||||
|
next if /^svn:(?:author|date|log)$/;
|
||||||
|
print $un " rev_prop: ", uri_encode($_), ' ',
|
||||||
|
uri_encode($rp->{$_}), "\n";
|
||||||
|
}
|
||||||
|
$pool->clear;
|
||||||
|
close $un or croak $!;
|
||||||
|
|
||||||
|
{ revision => $rev, date => "+0000 $Y-$m-$d $H:$M:$S",
|
||||||
|
author => $author, msg => $msg."\n", parents => $parents || [],
|
||||||
|
revprops => $rp }
|
||||||
}
|
}
|
||||||
|
|
||||||
sub process_rm {
|
sub process_rm {
|
||||||
|
@ -2953,9 +3062,11 @@ sub process_rm {
|
||||||
}
|
}
|
||||||
print "\tD\t$f/\n" unless $q;
|
print "\tD\t$f/\n" unless $q;
|
||||||
close $ls or croak $?;
|
close $ls or croak $?;
|
||||||
|
return $SVN::Node::dir;
|
||||||
} else {
|
} else {
|
||||||
print $gui '0 ',0 x 40,"\t",$f,"\0" or croak $!;
|
print $gui '0 ',0 x 40,"\t",$f,"\0" or croak $!;
|
||||||
print "\tD\t$f\n" unless $q;
|
print "\tD\t$f\n" unless $q;
|
||||||
|
return $SVN::Node::file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2976,13 +3087,14 @@ sub libsvn_fetch_delta {
|
||||||
unless ($ed->{git_commit_ok}) {
|
unless ($ed->{git_commit_ok}) {
|
||||||
die "SVN connection failed somewhere...\n";
|
die "SVN connection failed somewhere...\n";
|
||||||
}
|
}
|
||||||
libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]);
|
libsvn_log_entry($rev, $author, $date, $msg, [$last_commit], $ed);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub libsvn_fetch_full {
|
sub libsvn_fetch_full {
|
||||||
my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
|
my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
|
||||||
open my $gui, '| git-update-index -z --index-info' or croak $!;
|
open my $gui, '| git-update-index -z --index-info' or croak $!;
|
||||||
my %amr;
|
my %amr;
|
||||||
|
my $ut = { empty => {}, dir_prop => {}, file_prop => {} };
|
||||||
my $p = $SVN->{svn_path};
|
my $p = $SVN->{svn_path};
|
||||||
foreach my $f (keys %$paths) {
|
foreach my $f (keys %$paths) {
|
||||||
my $m = $paths->{$f}->action();
|
my $m = $paths->{$f}->action();
|
||||||
|
@ -2993,8 +3105,11 @@ sub libsvn_fetch_full {
|
||||||
$f =~ s#^/##;
|
$f =~ s#^/##;
|
||||||
}
|
}
|
||||||
if ($m =~ /^[DR]$/) {
|
if ($m =~ /^[DR]$/) {
|
||||||
process_rm($gui, $last_commit, $f, $_q);
|
my $t = process_rm($gui, $last_commit, $f, $_q);
|
||||||
next if $m eq 'D';
|
if ($m eq 'D') {
|
||||||
|
$ut->{empty}->{$f} = 0 if $t == $SVN::Node::dir;
|
||||||
|
next;
|
||||||
|
}
|
||||||
# 'R' can be file replacements, too, right?
|
# 'R' can be file replacements, too, right?
|
||||||
}
|
}
|
||||||
my $pool = SVN::Pool->new;
|
my $pool = SVN::Pool->new;
|
||||||
|
@ -3007,18 +3122,32 @@ sub libsvn_fetch_full {
|
||||||
}
|
}
|
||||||
} elsif ($t == $SVN::Node::dir && $m =~ /^[AR]$/) {
|
} elsif ($t == $SVN::Node::dir && $m =~ /^[AR]$/) {
|
||||||
my @traversed = ();
|
my @traversed = ();
|
||||||
libsvn_traverse($gui, '', $f, $rev, \@traversed);
|
libsvn_traverse($gui, '', $f, $rev, \@traversed, $ut);
|
||||||
foreach (@traversed) {
|
if (@traversed) {
|
||||||
$amr{$_} = $m;
|
foreach (@traversed) {
|
||||||
|
$amr{$_} = $m;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
my ($dir, $file) = ($f =~ m#^(.*?)/?([^/]+)$#);
|
||||||
|
delete $ut->{empty}->{$dir};
|
||||||
|
$ut->{empty}->{$f} = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$pool->clear;
|
$pool->clear;
|
||||||
}
|
}
|
||||||
foreach (keys %amr) {
|
foreach (keys %amr) {
|
||||||
libsvn_get_file($gui, $_, $rev, $amr{$_});
|
libsvn_get_file($gui, $_, $rev, $amr{$_}, $ut);
|
||||||
|
my ($d) = ($_ =~ m#^(.*?)/?(?:[^/]+)$#);
|
||||||
|
delete $ut->{empty}->{$d};
|
||||||
|
}
|
||||||
|
unless (exists $ut->{dir_prop}->{''}) {
|
||||||
|
my $pool = SVN::Pool->new;
|
||||||
|
my (undef, undef, $props) = $SVN->get_dir('', $rev, $pool);
|
||||||
|
%{$ut->{dir_prop}->{''}} = %$props;
|
||||||
|
$pool->clear;
|
||||||
}
|
}
|
||||||
close $gui or croak $?;
|
close $gui or croak $?;
|
||||||
return libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]);
|
libsvn_log_entry($rev, $author, $date, $msg, [$last_commit], $ut);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub svn_grab_base_rev {
|
sub svn_grab_base_rev {
|
||||||
|
@ -3079,25 +3208,38 @@ sub libsvn_parse_revision {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub libsvn_traverse {
|
sub libsvn_traverse {
|
||||||
my ($gui, $pfx, $path, $rev, $files) = @_;
|
my ($gui, $pfx, $path, $rev, $files, $untracked) = @_;
|
||||||
my $cwd = length $pfx ? "$pfx/$path" : $path;
|
my $cwd = length $pfx ? "$pfx/$path" : $path;
|
||||||
my $pool = SVN::Pool->new;
|
my $pool = SVN::Pool->new;
|
||||||
$cwd =~ s#^\Q$SVN->{svn_path}\E##;
|
$cwd =~ s#^\Q$SVN->{svn_path}\E##;
|
||||||
|
my $nr = 0;
|
||||||
my ($dirent, $r, $props) = $SVN->get_dir($cwd, $rev, $pool);
|
my ($dirent, $r, $props) = $SVN->get_dir($cwd, $rev, $pool);
|
||||||
|
%{$untracked->{dir_prop}->{$cwd}} = %$props;
|
||||||
foreach my $d (keys %$dirent) {
|
foreach my $d (keys %$dirent) {
|
||||||
my $t = $dirent->{$d}->kind;
|
my $t = $dirent->{$d}->kind;
|
||||||
if ($t == $SVN::Node::dir) {
|
if ($t == $SVN::Node::dir) {
|
||||||
libsvn_traverse($gui, $cwd, $d, $rev, $files);
|
my $i = libsvn_traverse($gui, $cwd, $d, $rev,
|
||||||
|
$files, $untracked);
|
||||||
|
if ($i) {
|
||||||
|
$nr += $i;
|
||||||
|
} else {
|
||||||
|
$untracked->{empty}->{"$cwd/$d"} = 1;
|
||||||
|
}
|
||||||
} elsif ($t == $SVN::Node::file) {
|
} elsif ($t == $SVN::Node::file) {
|
||||||
|
$nr++;
|
||||||
my $file = "$cwd/$d";
|
my $file = "$cwd/$d";
|
||||||
if (defined $files) {
|
if (defined $files) {
|
||||||
push @$files, $file;
|
push @$files, $file;
|
||||||
} else {
|
} else {
|
||||||
libsvn_get_file($gui, $file, $rev, 'A');
|
libsvn_get_file($gui, $file, $rev, 'A',
|
||||||
|
$untracked);
|
||||||
|
my ($dir) = ($file =~ m#^(.*?)/?(?:[^/]+)$#);
|
||||||
|
delete $untracked->{empty}->{$dir};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$pool->clear;
|
$pool->clear;
|
||||||
|
$nr;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub libsvn_traverse_ignore {
|
sub libsvn_traverse_ignore {
|
||||||
|
@ -3197,12 +3339,26 @@ sub libsvn_find_parent_branch {
|
||||||
unlink $GIT_SVN_INDEX;
|
unlink $GIT_SVN_INDEX;
|
||||||
print STDERR "Found branch parent: ($GIT_SVN) $parent\n";
|
print STDERR "Found branch parent: ($GIT_SVN) $parent\n";
|
||||||
sys(qw/git-read-tree/, $parent);
|
sys(qw/git-read-tree/, $parent);
|
||||||
# I can't seem to get do_switch() to work correctly with
|
unless (libsvn_can_do_switch()) {
|
||||||
# the SWIG interface (TypeError when passing switch_url...),
|
return libsvn_fetch_full($parent, $paths, $rev,
|
||||||
# so we'll unconditionally bypass the delta interface here
|
$author, $date, $msg);
|
||||||
# for now
|
}
|
||||||
return libsvn_fetch_full($parent, $paths, $rev,
|
# do_switch works with svn/trunk >= r22312, but that is not
|
||||||
$author, $date, $msg);
|
# included with SVN 1.4.2 (the latest version at the moment),
|
||||||
|
# so we can't rely on it.
|
||||||
|
my $ra = libsvn_connect("$url/$branch_from");
|
||||||
|
my $ed = SVN::Git::Fetcher->new({c => $parent, q => $_q});
|
||||||
|
my $pool = SVN::Pool->new;
|
||||||
|
my $reporter = $ra->do_switch($rev, '', 1, $SVN->{url},
|
||||||
|
$ed, $pool);
|
||||||
|
my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
|
||||||
|
$reporter->set_path('', $r0, 0, @lock, $pool);
|
||||||
|
$reporter->finish_report($pool);
|
||||||
|
$pool->clear;
|
||||||
|
unless ($ed->{git_commit_ok}) {
|
||||||
|
die "SVN connection failed somewhere...\n";
|
||||||
|
}
|
||||||
|
return libsvn_log_entry($rev, $author, $date, $msg, [$parent]);
|
||||||
}
|
}
|
||||||
print STDERR "Nope, branch point not imported or unknown\n";
|
print STDERR "Nope, branch point not imported or unknown\n";
|
||||||
return undef;
|
return undef;
|
||||||
|
@ -3222,6 +3378,7 @@ sub libsvn_new_tree {
|
||||||
return $log_entry;
|
return $log_entry;
|
||||||
}
|
}
|
||||||
my ($paths, $rev, $author, $date, $msg) = @_;
|
my ($paths, $rev, $author, $date, $msg) = @_;
|
||||||
|
my $ut;
|
||||||
if ($_xfer_delta) {
|
if ($_xfer_delta) {
|
||||||
my $pool = SVN::Pool->new;
|
my $pool = SVN::Pool->new;
|
||||||
my $ed = SVN::Git::Fetcher->new({q => $_q});
|
my $ed = SVN::Git::Fetcher->new({q => $_q});
|
||||||
|
@ -3233,12 +3390,14 @@ sub libsvn_new_tree {
|
||||||
unless ($ed->{git_commit_ok}) {
|
unless ($ed->{git_commit_ok}) {
|
||||||
die "SVN connection failed somewhere...\n";
|
die "SVN connection failed somewhere...\n";
|
||||||
}
|
}
|
||||||
|
$ut = $ed;
|
||||||
} else {
|
} else {
|
||||||
|
$ut = { empty => {}, dir_prop => {}, file_prop => {} };
|
||||||
open my $gui, '| git-update-index -z --index-info' or croak $!;
|
open my $gui, '| git-update-index -z --index-info' or croak $!;
|
||||||
libsvn_traverse($gui, '', $SVN->{svn_path}, $rev);
|
libsvn_traverse($gui, '', $SVN->{svn_path}, $rev, undef, $ut);
|
||||||
close $gui or croak $?;
|
close $gui or croak $?;
|
||||||
}
|
}
|
||||||
return libsvn_log_entry($rev, $author, $date, $msg);
|
libsvn_log_entry($rev, $author, $date, $msg, [], $ut);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub find_graft_path_commit {
|
sub find_graft_path_commit {
|
||||||
|
@ -3423,13 +3582,28 @@ sub new {
|
||||||
$self->{gui} = $gui;
|
$self->{gui} = $gui;
|
||||||
$self->{c} = $git_svn->{c} if exists $git_svn->{c};
|
$self->{c} = $git_svn->{c} if exists $git_svn->{c};
|
||||||
$self->{q} = $git_svn->{q};
|
$self->{q} = $git_svn->{q};
|
||||||
|
$self->{empty} = {};
|
||||||
|
$self->{dir_prop} = {};
|
||||||
|
$self->{file_prop} = {};
|
||||||
|
$self->{absent_dir} = {};
|
||||||
|
$self->{absent_file} = {};
|
||||||
require Digest::MD5;
|
require Digest::MD5;
|
||||||
$self;
|
$self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub open_root {
|
||||||
|
{ path => '' };
|
||||||
|
}
|
||||||
|
|
||||||
|
sub open_directory {
|
||||||
|
my ($self, $path, $pb, $rev) = @_;
|
||||||
|
{ path => $path };
|
||||||
|
}
|
||||||
|
|
||||||
sub delete_entry {
|
sub delete_entry {
|
||||||
my ($self, $path, $rev, $pb) = @_;
|
my ($self, $path, $rev, $pb) = @_;
|
||||||
process_rm($self->{gui}, $self->{c}, $path, $self->{q});
|
my $t = process_rm($self->{gui}, $self->{c}, $path, $self->{q});
|
||||||
|
$self->{empty}->{$path} = 0 if $t == $SVN::Node::dir;
|
||||||
undef;
|
undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3437,16 +3611,50 @@ sub open_file {
|
||||||
my ($self, $path, $pb, $rev) = @_;
|
my ($self, $path, $pb, $rev) = @_;
|
||||||
my ($mode, $blob) = (safe_qx('git-ls-tree',$self->{c},'--',$path)
|
my ($mode, $blob) = (safe_qx('git-ls-tree',$self->{c},'--',$path)
|
||||||
=~ /^(\d{6}) blob ([a-f\d]{40})\t/);
|
=~ /^(\d{6}) blob ([a-f\d]{40})\t/);
|
||||||
|
unless (defined $mode && defined $blob) {
|
||||||
|
die "$path was not found in commit $self->{c} (r$rev)\n";
|
||||||
|
}
|
||||||
{ path => $path, mode_a => $mode, mode_b => $mode, blob => $blob,
|
{ path => $path, mode_a => $mode, mode_b => $mode, blob => $blob,
|
||||||
pool => SVN::Pool->new, action => 'M' };
|
pool => SVN::Pool->new, action => 'M' };
|
||||||
}
|
}
|
||||||
|
|
||||||
sub add_file {
|
sub add_file {
|
||||||
my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
|
my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
|
||||||
|
my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
|
||||||
|
delete $self->{empty}->{$dir};
|
||||||
{ path => $path, mode_a => 100644, mode_b => 100644,
|
{ path => $path, mode_a => 100644, mode_b => 100644,
|
||||||
pool => SVN::Pool->new, action => 'A' };
|
pool => SVN::Pool->new, action => 'A' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub add_directory {
|
||||||
|
my ($self, $path, $cp_path, $cp_rev) = @_;
|
||||||
|
my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
|
||||||
|
delete $self->{empty}->{$dir};
|
||||||
|
$self->{empty}->{$path} = 1;
|
||||||
|
{ path => $path };
|
||||||
|
}
|
||||||
|
|
||||||
|
sub change_dir_prop {
|
||||||
|
my ($self, $db, $prop, $value) = @_;
|
||||||
|
$self->{dir_prop}->{$db->{path}} ||= {};
|
||||||
|
$self->{dir_prop}->{$db->{path}}->{$prop} = $value;
|
||||||
|
undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub absent_directory {
|
||||||
|
my ($self, $path, $pb) = @_;
|
||||||
|
$self->{absent_dir}->{$pb->{path}} ||= [];
|
||||||
|
push @{$self->{absent_dir}->{$pb->{path}}}, $path;
|
||||||
|
undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub absent_file {
|
||||||
|
my ($self, $path, $pb) = @_;
|
||||||
|
$self->{absent_file}->{$pb->{path}} ||= [];
|
||||||
|
push @{$self->{absent_file}->{$pb->{path}}}, $path;
|
||||||
|
undef;
|
||||||
|
}
|
||||||
|
|
||||||
sub change_file_prop {
|
sub change_file_prop {
|
||||||
my ($self, $fb, $prop, $value) = @_;
|
my ($self, $fb, $prop, $value) = @_;
|
||||||
if ($prop eq 'svn:executable') {
|
if ($prop eq 'svn:executable') {
|
||||||
|
@ -3455,6 +3663,9 @@ sub change_file_prop {
|
||||||
}
|
}
|
||||||
} elsif ($prop eq 'svn:special') {
|
} elsif ($prop eq 'svn:special') {
|
||||||
$fb->{mode_b} = defined $value ? 120000 : 100644;
|
$fb->{mode_b} = defined $value ? 120000 : 100644;
|
||||||
|
} else {
|
||||||
|
$self->{file_prop}->{$fb->{path}} ||= {};
|
||||||
|
$self->{file_prop}->{$fb->{path}}->{$prop} = $value;
|
||||||
}
|
}
|
||||||
undef;
|
undef;
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@
|
||||||
# To disable system wide have in $GITWEB_CONFIG
|
# To disable system wide have in $GITWEB_CONFIG
|
||||||
# $feature{'snapshot'}{'default'} = [undef];
|
# $feature{'snapshot'}{'default'} = [undef];
|
||||||
# To have project specific config enable override in $GITWEB_CONFIG
|
# To have project specific config enable override in $GITWEB_CONFIG
|
||||||
# $feature{'blame'}{'override'} = 1;
|
# $feature{'snapshot'}{'override'} = 1;
|
||||||
# and in project config gitweb.snapshot = none|gzip|bzip2;
|
# and in project config gitweb.snapshot = none|gzip|bzip2;
|
||||||
'snapshot' => {
|
'snapshot' => {
|
||||||
'sub' => \&feature_snapshot,
|
'sub' => \&feature_snapshot,
|
||||||
|
@ -3229,10 +3229,13 @@ sub git_blob {
|
||||||
open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
|
open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
|
||||||
or die_error(undef, "Couldn't cat $file_name, $hash");
|
or die_error(undef, "Couldn't cat $file_name, $hash");
|
||||||
my $mimetype = blob_mimetype($fd, $file_name);
|
my $mimetype = blob_mimetype($fd, $file_name);
|
||||||
if ($mimetype !~ m/^text\//) {
|
if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)!) {
|
||||||
close $fd;
|
close $fd;
|
||||||
return git_blob_plain($mimetype);
|
return git_blob_plain($mimetype);
|
||||||
}
|
}
|
||||||
|
# we can have blame only for text/* mimetype
|
||||||
|
$have_blame &&= ($mimetype =~ m!^text/!);
|
||||||
|
|
||||||
git_header_html(undef, $expires);
|
git_header_html(undef, $expires);
|
||||||
my $formats_nav = '';
|
my $formats_nav = '';
|
||||||
if (defined $hash_base && (my %co = parse_commit($hash_base))) {
|
if (defined $hash_base && (my %co = parse_commit($hash_base))) {
|
||||||
|
@ -3269,13 +3272,24 @@ sub git_blob {
|
||||||
}
|
}
|
||||||
git_print_page_path($file_name, "blob", $hash_base);
|
git_print_page_path($file_name, "blob", $hash_base);
|
||||||
print "<div class=\"page_body\">\n";
|
print "<div class=\"page_body\">\n";
|
||||||
my $nr;
|
if ($mimetype =~ m!^text/!) {
|
||||||
while (my $line = <$fd>) {
|
my $nr;
|
||||||
chomp $line;
|
while (my $line = <$fd>) {
|
||||||
$nr++;
|
chomp $line;
|
||||||
$line = untabify($line);
|
$nr++;
|
||||||
printf "<div class=\"pre\"><a id=\"l%i\" href=\"#l%i\" class=\"linenr\">%4i</a> %s</div>\n",
|
$line = untabify($line);
|
||||||
$nr, $nr, $nr, esc_html($line, -nbsp=>1);
|
printf "<div class=\"pre\"><a id=\"l%i\" href=\"#l%i\" class=\"linenr\">%4i</a> %s</div>\n",
|
||||||
|
$nr, $nr, $nr, esc_html($line, -nbsp=>1);
|
||||||
|
}
|
||||||
|
} elsif ($mimetype =~ m!^image/!) {
|
||||||
|
print qq!<img type="$mimetype"!;
|
||||||
|
if ($file_name) {
|
||||||
|
print qq! alt="$file_name" title="$file_name"!;
|
||||||
|
}
|
||||||
|
print qq! src="! .
|
||||||
|
href(action=>"blob_plain", hash=>$hash,
|
||||||
|
hash_base=>$hash_base, file_name=>$file_name) .
|
||||||
|
qq!" />\n!;
|
||||||
}
|
}
|
||||||
close $fd
|
close $fd
|
||||||
or print "Reading blob failed.\n";
|
or print "Reading blob failed.\n";
|
||||||
|
@ -4282,7 +4296,7 @@ sub git_feed {
|
||||||
}
|
}
|
||||||
if (defined $logo_url) {
|
if (defined $logo_url) {
|
||||||
# not twice as wide as tall: 72 x 27 pixels
|
# not twice as wide as tall: 72 x 27 pixels
|
||||||
print "<logo>" . esc_url($logo_url) . "</logo>\n";
|
print "<logo>" . esc_url($logo) . "</logo>\n";
|
||||||
}
|
}
|
||||||
if (! %latest_date) {
|
if (! %latest_date) {
|
||||||
# dummy date to keep the feed valid until commits trickle in:
|
# dummy date to keep the feed valid until commits trickle in:
|
||||||
|
|
|
@ -96,7 +96,7 @@ static void flush(void)
|
||||||
if (output_fd >= 0)
|
if (output_fd >= 0)
|
||||||
write_or_die(output_fd, input_buffer, input_offset);
|
write_or_die(output_fd, input_buffer, input_offset);
|
||||||
SHA1_Update(&input_ctx, input_buffer, input_offset);
|
SHA1_Update(&input_ctx, input_buffer, input_offset);
|
||||||
memcpy(input_buffer, input_buffer + input_offset, input_len);
|
memmove(input_buffer, input_buffer + input_offset, input_len);
|
||||||
input_offset = 0;
|
input_offset = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1222,7 +1222,7 @@ static int merge(struct commit *h1,
|
||||||
|
|
||||||
tree->object.parsed = 1;
|
tree->object.parsed = 1;
|
||||||
tree->object.type = OBJ_TREE;
|
tree->object.type = OBJ_TREE;
|
||||||
hash_sha1_file(NULL, 0, tree_type, tree->object.sha1);
|
write_sha1_file(NULL, 0, tree_type, tree->object.sha1);
|
||||||
merged_common_ancestors = make_virtual_commit(tree, "ancestor");
|
merged_common_ancestors = make_virtual_commit(tree, "ancestor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
Makefile
|
perl.mak
|
||||||
|
perl.mak.old
|
||||||
blib
|
blib
|
||||||
blibdirs
|
blibdirs
|
||||||
pm_to_blib
|
pm_to_blib
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
#
|
||||||
|
# Makefile for perl support modules and routine
|
||||||
|
#
|
||||||
|
makfile:=perl.mak
|
||||||
|
|
||||||
|
PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
|
||||||
|
prefix_SQ = $(subst ','\'',$(prefix))
|
||||||
|
|
||||||
|
all install instlibdir: $(makfile)
|
||||||
|
$(MAKE) -f $(makfile) $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
test -f $(makfile) && $(MAKE) -f $(makfile) $@ || exit 0
|
||||||
|
$(RM) ppport.h
|
||||||
|
$(RM) $(makfile)
|
||||||
|
$(RM) $(makfile).old
|
||||||
|
|
||||||
|
ifdef NO_PERL_MAKEMAKER
|
||||||
|
instdir_SQ = $(subst ','\'',$(prefix)/lib)
|
||||||
|
$(makfile): ../GIT-CFLAGS Makefile
|
||||||
|
echo all: > $@
|
||||||
|
echo ' :' >> $@
|
||||||
|
echo install: >> $@
|
||||||
|
echo ' mkdir -p $(instdir_SQ)' >> $@
|
||||||
|
echo ' $(RM) $(instdir_SQ)/Git.pm; cp Git.pm $(instdir_SQ)' >> $@
|
||||||
|
echo ' $(RM) $(instdir_SQ)/Error.pm; \
|
||||||
|
cp private-Error.pm $(instdir_SQ)/Error.pm' >> $@
|
||||||
|
echo instlibdir: >> $@
|
||||||
|
echo ' echo $(instdir_SQ)' >> $@
|
||||||
|
else
|
||||||
|
$(makfile): Makefile.PL ../GIT-CFLAGS
|
||||||
|
'$(PERL_PATH_SQ)' $< PREFIX='$(prefix_SQ)'
|
||||||
|
endif
|
||||||
|
|
||||||
|
# this is just added comfort for calling make directly in perl dir
|
||||||
|
# (even though GIT-CFLAGS aren't used yet. If ever)
|
||||||
|
../GIT-CFLAGS:
|
||||||
|
$(MAKE) -C .. GIT-CFLAGS
|
||||||
|
|
|
@ -24,5 +24,6 @@ WriteMakefile(
|
||||||
NAME => 'Git',
|
NAME => 'Git',
|
||||||
VERSION_FROM => 'Git.pm',
|
VERSION_FROM => 'Git.pm',
|
||||||
PM => \%pm,
|
PM => \%pm,
|
||||||
|
MAKEFILE => 'perl.mak',
|
||||||
%extra
|
%extra
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
|
static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
|
||||||
|
|
||||||
static int deny_non_fast_forwards = 0;
|
static int deny_non_fast_forwards = 0;
|
||||||
static int unpack_limit = 5000;
|
static int unpack_limit = 100;
|
||||||
static int report_status;
|
static int report_status;
|
||||||
|
|
||||||
static char capabilities[] = " report-status delete-refs ";
|
static char capabilities[] = " report-status delete-refs ";
|
||||||
|
@ -120,7 +120,8 @@ static int update(struct command *cmd)
|
||||||
"but I can't find it!", new_hex);
|
"but I can't find it!", new_hex);
|
||||||
}
|
}
|
||||||
if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
|
if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
|
||||||
!is_null_sha1(old_sha1)) {
|
!is_null_sha1(old_sha1) &&
|
||||||
|
!strncmp(name, "refs/heads/", 11)) {
|
||||||
struct commit *old_commit, *new_commit;
|
struct commit *old_commit, *new_commit;
|
||||||
struct commit_list *bases, *ent;
|
struct commit_list *bases, *ent;
|
||||||
|
|
||||||
|
|
|
@ -109,12 +109,10 @@ index d99af23..8b32fb5 100644
|
||||||
+ whitespace at beginning
|
+ whitespace at beginning
|
||||||
whitespace change
|
whitespace change
|
||||||
-whitespace in the middle
|
-whitespace in the middle
|
||||||
-whitespace at end
|
|
||||||
+white space in the middle
|
+white space in the middle
|
||||||
+whitespace at end
|
whitespace at end
|
||||||
unchanged line
|
unchanged line
|
||||||
-CR at endQ
|
CR at endQ
|
||||||
+CR at end
|
|
||||||
EOF
|
EOF
|
||||||
git-diff -b > out
|
git-diff -b > out
|
||||||
test_expect_success 'another test, with -b' 'diff -u expect out'
|
test_expect_success 'another test, with -b' 'diff -u expect out'
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='Test merge without common ancestors'
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
# This scenario is based on a real-world repository of Shawn Pearce.
|
||||||
|
|
||||||
|
# 1 - A - D - F
|
||||||
|
# \ X /
|
||||||
|
# B X
|
||||||
|
# X \
|
||||||
|
# 2 - C - E - G
|
||||||
|
|
||||||
|
export GIT_COMMITTER_DATE="2006-12-12 23:28:00 +0100"
|
||||||
|
echo 1 > a1
|
||||||
|
git add a1
|
||||||
|
GIT_AUTHOR_DATE="2006-12-12 23:00:00" git commit -m 1 a1
|
||||||
|
|
||||||
|
git checkout -b A master
|
||||||
|
echo A > a1
|
||||||
|
GIT_AUTHOR_DATE="2006-12-12 23:00:01" git commit -m A a1
|
||||||
|
|
||||||
|
git checkout -b B master
|
||||||
|
echo B > a1
|
||||||
|
GIT_AUTHOR_DATE="2006-12-12 23:00:02" git commit -m B a1
|
||||||
|
|
||||||
|
git checkout -b D A
|
||||||
|
git-rev-parse B > .git/MERGE_HEAD
|
||||||
|
echo D > a1
|
||||||
|
git update-index a1
|
||||||
|
GIT_AUTHOR_DATE="2006-12-12 23:00:03" git commit -m D
|
||||||
|
|
||||||
|
git symbolic-ref HEAD refs/heads/other
|
||||||
|
echo 2 > a1
|
||||||
|
GIT_AUTHOR_DATE="2006-12-12 23:00:04" git commit -m 2 a1
|
||||||
|
|
||||||
|
git checkout -b C
|
||||||
|
echo C > a1
|
||||||
|
GIT_AUTHOR_DATE="2006-12-12 23:00:05" git commit -m C a1
|
||||||
|
|
||||||
|
git checkout -b E C
|
||||||
|
git-rev-parse B > .git/MERGE_HEAD
|
||||||
|
echo E > a1
|
||||||
|
git update-index a1
|
||||||
|
GIT_AUTHOR_DATE="2006-12-12 23:00:06" git commit -m E
|
||||||
|
|
||||||
|
git checkout -b G E
|
||||||
|
git-rev-parse A > .git/MERGE_HEAD
|
||||||
|
echo G > a1
|
||||||
|
git update-index a1
|
||||||
|
GIT_AUTHOR_DATE="2006-12-12 23:00:07" git commit -m G
|
||||||
|
|
||||||
|
git checkout -b F D
|
||||||
|
git-rev-parse C > .git/MERGE_HEAD
|
||||||
|
echo F > a1
|
||||||
|
git update-index a1
|
||||||
|
GIT_AUTHOR_DATE="2006-12-12 23:00:08" git commit -m F
|
||||||
|
|
||||||
|
test_expect_failure "combined merge conflicts" "git merge -m final G"
|
||||||
|
|
||||||
|
git ls-files --stage > out
|
||||||
|
cat > expect << EOF
|
||||||
|
100644 f70f10e4db19068f79bc43844b49f3eece45c4e8 1 a1
|
||||||
|
100644 cf84443e49e1b366fac938711ddf4be2d4d1d9e9 2 a1
|
||||||
|
100644 fd7923529855d0b274795ae3349c5e0438333979 3 a1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success "virtual trees were processed" "diff -u expect out"
|
||||||
|
|
||||||
|
test_done
|
|
@ -105,4 +105,17 @@ test_expect_success "Michael Cassar's test case" '
|
||||||
}
|
}
|
||||||
'
|
'
|
||||||
|
|
||||||
|
rm -fr papers partA path?
|
||||||
|
|
||||||
|
test_expect_success "Sergey Vlasov's test case" '
|
||||||
|
rm -fr .git &&
|
||||||
|
git init-db &&
|
||||||
|
mkdir ab &&
|
||||||
|
date >ab.c &&
|
||||||
|
date >ab/d &&
|
||||||
|
git add ab.c ab &&
|
||||||
|
git commit -m 'initial' &&
|
||||||
|
git mv ab a
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
|
@ -142,4 +142,20 @@ test_expect_success \
|
||||||
diff F/newfile6.png ../F/newfile6.png
|
diff F/newfile6.png ../F/newfile6.png
|
||||||
)'
|
)'
|
||||||
|
|
||||||
|
test_expect_success 'Retain execute bit' '
|
||||||
|
mkdir G &&
|
||||||
|
echo executeon >G/on &&
|
||||||
|
chmod +x G/on &&
|
||||||
|
echo executeoff >G/off &&
|
||||||
|
git add G/on &&
|
||||||
|
git add G/off &&
|
||||||
|
git commit -a -m "Execute test" &&
|
||||||
|
(
|
||||||
|
cd "$CVSWORK" &&
|
||||||
|
git-cvsexportcommit -c HEAD
|
||||||
|
test -x G/on &&
|
||||||
|
! test -x G/off
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
|
@ -370,7 +370,7 @@ int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
|
||||||
int i;
|
int i;
|
||||||
struct object_list *posn = trees;
|
struct object_list *posn = trees;
|
||||||
struct tree_entry_list df_conflict_list;
|
struct tree_entry_list df_conflict_list;
|
||||||
struct cache_entry df_conflict_entry;
|
static struct cache_entry *dfc;
|
||||||
|
|
||||||
memset(&df_conflict_list, 0, sizeof(df_conflict_list));
|
memset(&df_conflict_list, 0, sizeof(df_conflict_list));
|
||||||
df_conflict_list.next = &df_conflict_list;
|
df_conflict_list.next = &df_conflict_list;
|
||||||
|
@ -381,8 +381,10 @@ int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
|
||||||
state.refresh_cache = 1;
|
state.refresh_cache = 1;
|
||||||
|
|
||||||
o->merge_size = len;
|
o->merge_size = len;
|
||||||
memset(&df_conflict_entry, 0, sizeof(df_conflict_entry));
|
|
||||||
o->df_conflict_entry = &df_conflict_entry;
|
if (!dfc)
|
||||||
|
dfc = xcalloc(1, sizeof(struct cache_entry) + 1);
|
||||||
|
o->df_conflict_entry = dfc;
|
||||||
|
|
||||||
if (len) {
|
if (len) {
|
||||||
posns = xmalloc(len * sizeof(struct tree_entry_list *));
|
posns = xmalloc(len * sizeof(struct tree_entry_list *));
|
||||||
|
|
|
@ -230,7 +230,8 @@ unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
|
||||||
while (ptr + 1 < top && isspace(ptr[1])
|
while (ptr + 1 < top && isspace(ptr[1])
|
||||||
&& ptr[1] != '\n')
|
&& ptr[1] != '\n')
|
||||||
ptr++;
|
ptr++;
|
||||||
if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
|
if (flags & XDF_IGNORE_WHITESPACE_CHANGE
|
||||||
|
&& ptr[1] != '\n') {
|
||||||
ha += (ha << 5);
|
ha += (ha << 5);
|
||||||
ha ^= (unsigned long) ' ';
|
ha ^= (unsigned long) ' ';
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue