1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-05-12 14:06:08 +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:
Junio C Hamano 2006-12-12 21:52:19 -08:00
commit 8042ed1ceb
31 changed files with 685 additions and 373 deletions

View File

@ -56,8 +56,8 @@ man7: $(DOC_MAN7)
install: man
$(INSTALL) -d -m755 $(DESTDIR)$(man1dir) $(DESTDIR)$(man7dir)
$(INSTALL) $(DOC_MAN1) $(DESTDIR)$(man1dir)
$(INSTALL) $(DOC_MAN7) $(DESTDIR)$(man7dir)
$(INSTALL) -m644 $(DOC_MAN1) $(DESTDIR)$(man1dir)
$(INSTALL) -m644 $(DOC_MAN7) $(DESTDIR)$(man7dir)
#

View File

@ -125,10 +125,17 @@ apply.whitespace::
branch.<name>.remote::
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::
When in branch <name>, it tells `git fetch` the default remote branch
to be merged.
When in branch <name>, it tells `git fetch` the default refspec to
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::
A boolean to enable/disable colored output when the pager is in

View File

@ -1,43 +1,122 @@
git for CVS users
=================
So you're a CVS user. That's OK, it's a treatable condition. The job of
this document is to put you on the road to recovery, by helping you
convert an existing cvs repository to git, and by showing you how to use a
git repository in a cvs-like fashion.
Git differs from CVS in that every working tree contains a repository with
a full copy of the project history, and no repository is inherently more
important than any other. However, you can emulate the CVS model by
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
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
any other. However, you can emulate the CVS model by designating a
single shared repository which people can synchronize with; see below
for details.
------------------------------------------------
$ git pull origin
------------------------------------------------
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
-----------------------
First, install version 2.1 or higher of cvsps from
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
<destination>, which will be created if necessary. The -v option makes
the conversion script very chatty.
<destination>, which will be created if necessary.
The import checks out from CVS every revision of every file. Reportedly
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
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
a common repository. In the next section we'll explain how to do this
with git. However, the distributed nature of git allows other development
models, and you may want to first consider whether one of them might be a
better fit for your project.
a common repository. As we've seen, this is also possible with git.
However, the distributed nature of git allows other development models,
and you may want to first consider whether one of them might be a better
fit for your project.
For example, you can choose a single person to maintain the project's
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
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.

View File

@ -129,5 +129,21 @@
-a::
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
link:diffcore.html[diffcore documentation].

View File

@ -11,27 +11,26 @@ SYNOPSIS
[verse]
'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
[-o <name>] [-u <upload-pack>] [--reference <repository>]
[--use-separate-remote | --use-immingled-remote] <repository>
[--use-separate-remote | --no-separate-remote] <repository>
[<directory>]
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
this line:
Clones a repository into a newly created directory, creates
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
This is to help the typical workflow of working off of the
remote `master` branch. Every time `git pull` without argument
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.
After the clone, a plain `git fetch` without arguments will update
all the remote-tracking branches, and a `git pull` without
arguments will in addition merge the remote master branch into the
current branch.
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
-------
@ -105,7 +104,7 @@ OPTIONS
of `$GIT_DIR/refs/heads/`. Only the local master branch is
saved in the latter. This is the default.
--use-immingled-remote::
--no-separate-remote::
Save remotes heads in the same namespace as the local
heads, `$GIT_DIR/refs/heads/'. In regular repositories,
this is a legacy setup git-clone created by default in

View File

@ -57,11 +57,13 @@ See '<<fetch-args,Additional Fetch Arguments>>' if you are interested in
manually joining branches on commit.
'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
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
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
cleaner, more linear history.

View File

@ -91,6 +91,10 @@ all:
#
# 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.
#
# 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
@$(SHELL_PATH) ./GIT-VERSION-GEN
@ -323,18 +327,6 @@ ifeq ($(uname_S),Darwin)
NEEDS_SSL_WITH_CRYPTO = YesPlease
NEEDS_LIBICONV = 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
ifeq ($(uname_S),SunOS)
NEEDS_SOCKET = YesPlease
@ -412,6 +404,21 @@ endif
-include config.mak.autogen
-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
ifdef CURLDIR
# This is still problematic -- gcc does not always want -R.
@ -540,6 +547,9 @@ endif
ifdef NO_ACCURATE_DIFF
BASIC_CFLAGS += -DNO_ACCURATE_DIFF
endif
ifdef NO_PERL_MAKEMAKER
export NO_PERL_MAKEMAKER
endif
# 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: perl/Makefile
$(MAKE) -C perl
all:
$(MAKE) -C perl PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
$(MAKE) -C templates
strip: $(PROGRAMS) git$X
@ -603,7 +613,11 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
chmod +x $@+
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
rm -f $@ $@+
INSTLIBDIR=`$(MAKE) -C perl -s --no-print-directory instlibdir` && \
@ -798,7 +812,7 @@ install: all
$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
$(INSTALL) git$X gitk '$(DESTDIR_SQ)$(bindir_SQ)'
$(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)'; \
then \
ln -f '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \
@ -868,8 +882,7 @@ clean:
rm -f $(htmldocs).tar.gz $(manpages).tar.gz
rm -f gitweb/gitweb.cgi
$(MAKE) -C Documentation/ clean
[ ! -f perl/Makefile ] || $(MAKE) -C perl/ clean || $(MAKE) -C perl/ clean
rm -f perl/ppport.h perl/Makefile.old
$(MAKE) -C perl clean
$(MAKE) -C templates/ clean
$(MAKE) -C t/ clean
rm -f GIT-VERSION-FILE GIT-CFLAGS

View File

@ -146,21 +146,24 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
&& lstat(dst, &st) == 0)
bad = "cannot move directory over file";
else if (src_is_dir) {
const char *src_w_slash = add_slash(src);
int len_w_slash = length + 1;
int first, last;
modes[i] = WORKING_DIRECTORY;
first = cache_name_pos(src, length);
first = cache_name_pos(src_w_slash, len_w_slash);
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;
for (last = first; last < active_nr; last++) {
const char *path = active_cache[last]->name;
if (strncmp(path, src, length)
|| path[length] != '/')
if (strncmp(path, src_w_slash, len_w_slash))
break;
}
free((char *)src_w_slash);
if (last - first < 1)
bad = "source directory is empty";

View File

@ -188,18 +188,25 @@ static void read_from_stdin(struct path_list *list)
bob = buffer + strlen(buffer);
else {
offset = 8;
while (isspace(bob[-1]))
while (buffer + offset < bob &&
isspace(bob[-1]))
bob--;
}
while (fgets(buffer2, sizeof(buffer2), stdin) &&
buffer2[0] != '\n')
; /* 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,
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;
authorlen = strlen(scratch);
} else {
while (bracket[-1] == ' ')
if (bracket[-1] == ' ')
bracket--;
author = buffer + 7;

15
fetch.c
View File

@ -22,14 +22,15 @@ void pull_say(const char *fmt, const char *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];
strcpy(missing_hex, sha1_to_hex(missing));;
fprintf(stderr,
"Cannot obtain needed %s %s\nwhile processing commit %s.\n",
what, missing_hex, sha1_to_hex(current_commit_sha1));
strcpy(missing_hex, sha1_to_hex(obj->sha1));;
fprintf(stderr, "Cannot obtain needed %s %s\n",
obj->type ? typename(obj->type): "object", missing_hex);
if (!is_null_sha1(current_commit_sha1))
fprintf(stderr, "while processing commit %s.\n",
sha1_to_hex(current_commit_sha1));
}
static int process(struct object *obj);
@ -177,7 +178,7 @@ static int loop(void)
*/
if (! (obj->flags & TO_SCAN)) {
if (fetch(obj->sha1)) {
report_missing(typename(obj->type), obj->sha1);
report_missing(obj);
return -1;
}
}

View File

@ -14,7 +14,7 @@ die() {
}
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() {
@ -140,7 +140,7 @@ while
*,--use-separate-remote)
# default
use_separate_remote=t ;;
*,--use-immingled-remote)
*,--no-separate-remote)
use_separate_remote= ;;
1,--reference) usage ;;
*,--reference)
@ -176,7 +176,7 @@ repo="$1"
test -n "$repo" ||
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"
then
if test yes = "$origin_override"
@ -377,9 +377,9 @@ then
*) origin_track="$remote_top/$origin"
git-update-ref "refs/heads/$origin" "$head_sha1" ;;
esac &&
echo >"$GIT_DIR/remotes/$origin" \
"URL: $repo
Pull: refs/heads/$head_points_at:$origin_track" &&
git-repo-config remote."$origin".url "$repo" &&
git-repo-config remote."$origin".fetch \
"refs/heads/$head_points_at:$origin_track" &&
(cd "$GIT_DIR/$remote_top" && find . -type f -print) |
while read dotslref
do
@ -393,8 +393,8 @@ Pull: refs/heads/$head_points_at:$origin_track" &&
then
continue
fi
echo "Pull: refs/heads/${name}:$remote_top/${name}"
done >>"$GIT_DIR/remotes/$origin" &&
git-repo-config remote."$origin".fetch "refs/heads/${name}:$remote_top/${name}" '^$'
done &&
case "$use_separate_remote" in
t)
rm -f "refs/remotes/$origin/HEAD"

View File

@ -116,6 +116,7 @@
close MSG;
my (@afiles, @dfiles, @mfiles, @dirs);
my %amodes;
my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
#print @files;
$? && die "Error in git-diff-tree";
@ -124,6 +125,7 @@
my @fields = split(m!\s+!, $f);
if ($fields[4] eq 'A') {
my $path = $fields[5];
$amodes{$path} = $fields[1];
push @afiles, $path;
# add any needed parent directories
$path = dirname $path;
@ -268,6 +270,7 @@
}
foreach my $f (@afiles) {
set_new_file_permissions($f, $amodes{$f});
if (grep { $_ eq $f } @bfiles) {
system('cvs', 'add','-kb',$f);
} else {
@ -342,3 +345,13 @@ sub safe_pipe_capture {
}
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";
}

View File

@ -17,6 +17,7 @@
use strict;
use warnings;
use bytes;
use Fcntl;
use File::Temp qw/tempdir tempfile/;

View File

@ -188,8 +188,9 @@ else
# in this loop.
merge_name=$(for remote
do
rh=$(git-rev-parse --verify "$remote"^0 2>/dev/null) &&
bh=$(git show-ref -s --verify "refs/heads/$remote") &&
rh=$(git-rev-parse --verify "$remote"^0 2>/dev/null) ||
continue ;# not something we can merge
bh=$(git show-ref -s --verify "refs/heads/$remote" 2>/dev/null)
if test "$rh" = "$bh"
then
echo "$rh branch '$remote' of ."

View File

@ -116,7 +116,7 @@ expand_refs_wildcard () {
while read sha1 name
do
mapped=${name#"$from"}
if test "z$name" != "z${name#'^{}'}" ||
if test "z$name" != "z${name%'^{}'}" ||
test "z$name" = "z$mapped"
then
continue
@ -134,6 +134,8 @@ canon_refs_list_for_fetch () {
# or the first one otherwise; add prefix . to the rest
# to prevent the secondary branches to be merged by default.
merge_branches=
found_mergeref=
curr_branch=
if test "$1" = "-d"
then
shift ; remote="$1" ; shift
@ -171,6 +173,10 @@ canon_refs_list_for_fetch () {
dot_prefix= && break
done
fi
if test -z $dot_prefix
then
found_mergeref=true
fi
case "$remote" in
'') remote=HEAD ;;
refs/heads/* | refs/tags/* | refs/remotes/*) ;;
@ -191,6 +197,11 @@ canon_refs_list_for_fetch () {
fi
echo "${dot_prefix}${force}${remote}:${local}"
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)

View File

@ -30,4 +30,4 @@ echo " $url"
echo
git log $baserev..$headrev | git-shortlog ;
git diff --stat --summary $baserev..$headrev
git diff -M --stat --summary $baserev..$headrev

View File

@ -63,6 +63,7 @@ case "$reset_type" in
;;
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

View File

@ -21,7 +21,17 @@
$ENV{LC_ALL} = 'C';
$| = 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
# optional and preserve the capability to use the command-line client.
# use eval { require SVN::... } to make it lazy load
@ -72,7 +82,7 @@ sub nag_lib {
$_username, $_config_dir, $_no_auth_cache, $_xfer_delta,
$_pager, $_color);
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 %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
@ -459,6 +469,7 @@ sub fetch_lib {
$min = $max + 1;
$max += $inc;
$max = $head if ($max > $head);
$SVN = libsvn_connect($SVN_URL);
}
restore_index($index);
return { revision => $last_rev, commit => $last_commit };
@ -593,8 +604,9 @@ sub commit_lib {
}
sub dcommit {
my $head = shift || 'HEAD';
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;
foreach my $d (reverse @refs) {
if (quiet_run('git-rev-parse','--verify',"$d~1") != 0) {
@ -621,16 +633,16 @@ sub dcommit {
}
return if $_dry_run;
fetch();
my @diff = safe_qx(qw/git-diff-tree HEAD/, $gs);
my @diff = safe_qx('git-diff-tree', $head, $gs);
my @finish;
if (@diff) {
@finish = qw/rebase/;
push @finish, qw/--merge/ if $_merge;
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 {
print "No changes between current HEAD and $gs\n",
"Hard resetting to the latest $gs\n";
print "No changes between current $head and $gs\n",
"Resetting to the latest $gs\n";
@finish = qw/reset --mixed/;
}
sys('git', @finish, $gs);
@ -2015,9 +2027,17 @@ sub git_commit {
# just in case we clobber the existing ref, we still want that ref
# 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;
}
open STDERR, '>&', $stderr or croak $!;
close $stderr or croak $!;
close $null or croak $!;
if (exists $tree_map{$tree}) {
foreach my $p (@{$tree_map{$tree}}) {
@ -2876,6 +2896,24 @@ sub libsvn_connect {
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 {
my ($ra) = @_;
SVN::Ra->new(map { $_ => $ra->{$_} } qw/config url
@ -2883,7 +2921,7 @@ sub libsvn_dup_ra {
}
sub libsvn_get_file {
my ($gui, $f, $rev, $chg) = @_;
my ($gui, $f, $rev, $chg, $untracked) = @_;
$f =~ s#^/##;
print "\t$chg\t$f\n" unless $_q;
@ -2921,11 +2959,25 @@ sub libsvn_get_file {
waitpid $pid, 0;
$hash =~ /^$sha1$/o or die "not a sha1: $hash\n";
}
%{$untracked->{file_prop}->{$f}} = %$props;
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 {
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
(\d\d)\:(\d\d)\:(\d\d).\d+Z$/x)
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";
}
$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 {
@ -2953,9 +3062,11 @@ sub process_rm {
}
print "\tD\t$f/\n" unless $q;
close $ls or croak $?;
return $SVN::Node::dir;
} else {
print $gui '0 ',0 x 40,"\t",$f,"\0" or croak $!;
print "\tD\t$f\n" unless $q;
return $SVN::Node::file;
}
}
@ -2976,13 +3087,14 @@ sub libsvn_fetch_delta {
unless ($ed->{git_commit_ok}) {
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 {
my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
open my $gui, '| git-update-index -z --index-info' or croak $!;
my %amr;
my $ut = { empty => {}, dir_prop => {}, file_prop => {} };
my $p = $SVN->{svn_path};
foreach my $f (keys %$paths) {
my $m = $paths->{$f}->action();
@ -2993,8 +3105,11 @@ sub libsvn_fetch_full {
$f =~ s#^/##;
}
if ($m =~ /^[DR]$/) {
process_rm($gui, $last_commit, $f, $_q);
next if $m eq 'D';
my $t = process_rm($gui, $last_commit, $f, $_q);
if ($m eq 'D') {
$ut->{empty}->{$f} = 0 if $t == $SVN::Node::dir;
next;
}
# 'R' can be file replacements, too, right?
}
my $pool = SVN::Pool->new;
@ -3007,18 +3122,32 @@ sub libsvn_fetch_full {
}
} elsif ($t == $SVN::Node::dir && $m =~ /^[AR]$/) {
my @traversed = ();
libsvn_traverse($gui, '', $f, $rev, \@traversed);
foreach (@traversed) {
$amr{$_} = $m;
libsvn_traverse($gui, '', $f, $rev, \@traversed, $ut);
if (@traversed) {
foreach (@traversed) {
$amr{$_} = $m;
}
} else {
my ($dir, $file) = ($f =~ m#^(.*?)/?([^/]+)$#);
delete $ut->{empty}->{$dir};
$ut->{empty}->{$f} = 1;
}
}
$pool->clear;
}
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 $?;
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 {
@ -3079,25 +3208,38 @@ sub libsvn_parse_revision {
}
sub libsvn_traverse {
my ($gui, $pfx, $path, $rev, $files) = @_;
my ($gui, $pfx, $path, $rev, $files, $untracked) = @_;
my $cwd = length $pfx ? "$pfx/$path" : $path;
my $pool = SVN::Pool->new;
$cwd =~ s#^\Q$SVN->{svn_path}\E##;
my $nr = 0;
my ($dirent, $r, $props) = $SVN->get_dir($cwd, $rev, $pool);
%{$untracked->{dir_prop}->{$cwd}} = %$props;
foreach my $d (keys %$dirent) {
my $t = $dirent->{$d}->kind;
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) {
$nr++;
my $file = "$cwd/$d";
if (defined $files) {
push @$files, $file;
} 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;
$nr;
}
sub libsvn_traverse_ignore {
@ -3197,12 +3339,26 @@ sub libsvn_find_parent_branch {
unlink $GIT_SVN_INDEX;
print STDERR "Found branch parent: ($GIT_SVN) $parent\n";
sys(qw/git-read-tree/, $parent);
# I can't seem to get do_switch() to work correctly with
# the SWIG interface (TypeError when passing switch_url...),
# so we'll unconditionally bypass the delta interface here
# for now
return libsvn_fetch_full($parent, $paths, $rev,
$author, $date, $msg);
unless (libsvn_can_do_switch()) {
return libsvn_fetch_full($parent, $paths, $rev,
$author, $date, $msg);
}
# do_switch works with svn/trunk >= r22312, but that is not
# 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";
return undef;
@ -3222,6 +3378,7 @@ sub libsvn_new_tree {
return $log_entry;
}
my ($paths, $rev, $author, $date, $msg) = @_;
my $ut;
if ($_xfer_delta) {
my $pool = SVN::Pool->new;
my $ed = SVN::Git::Fetcher->new({q => $_q});
@ -3233,12 +3390,14 @@ sub libsvn_new_tree {
unless ($ed->{git_commit_ok}) {
die "SVN connection failed somewhere...\n";
}
$ut = $ed;
} else {
$ut = { empty => {}, dir_prop => {}, file_prop => {} };
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 $?;
}
return libsvn_log_entry($rev, $author, $date, $msg);
libsvn_log_entry($rev, $author, $date, $msg, [], $ut);
}
sub find_graft_path_commit {
@ -3423,13 +3582,28 @@ sub new {
$self->{gui} = $gui;
$self->{c} = $git_svn->{c} if exists $git_svn->{c};
$self->{q} = $git_svn->{q};
$self->{empty} = {};
$self->{dir_prop} = {};
$self->{file_prop} = {};
$self->{absent_dir} = {};
$self->{absent_file} = {};
require Digest::MD5;
$self;
}
sub open_root {
{ path => '' };
}
sub open_directory {
my ($self, $path, $pb, $rev) = @_;
{ path => $path };
}
sub delete_entry {
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;
}
@ -3437,16 +3611,50 @@ sub open_file {
my ($self, $path, $pb, $rev) = @_;
my ($mode, $blob) = (safe_qx('git-ls-tree',$self->{c},'--',$path)
=~ /^(\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,
pool => SVN::Pool->new, action => 'M' };
}
sub add_file {
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,
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 {
my ($self, $fb, $prop, $value) = @_;
if ($prop eq 'svn:executable') {
@ -3455,6 +3663,9 @@ sub change_file_prop {
}
} elsif ($prop eq 'svn:special') {
$fb->{mode_b} = defined $value ? 120000 : 100644;
} else {
$self->{file_prop}->{$fb->{path}} ||= {};
$self->{file_prop}->{$fb->{path}}->{$prop} = $value;
}
undef;
}

View File

@ -120,7 +120,7 @@
# To disable system wide have in $GITWEB_CONFIG
# $feature{'snapshot'}{'default'} = [undef];
# 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;
'snapshot' => {
'sub' => \&feature_snapshot,
@ -3229,10 +3229,13 @@ sub git_blob {
open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
or die_error(undef, "Couldn't cat $file_name, $hash");
my $mimetype = blob_mimetype($fd, $file_name);
if ($mimetype !~ m/^text\//) {
if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)!) {
close $fd;
return git_blob_plain($mimetype);
}
# we can have blame only for text/* mimetype
$have_blame &&= ($mimetype =~ m!^text/!);
git_header_html(undef, $expires);
my $formats_nav = '';
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);
print "<div class=\"page_body\">\n";
my $nr;
while (my $line = <$fd>) {
chomp $line;
$nr++;
$line = untabify($line);
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);
if ($mimetype =~ m!^text/!) {
my $nr;
while (my $line = <$fd>) {
chomp $line;
$nr++;
$line = untabify($line);
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
or print "Reading blob failed.\n";
@ -4282,7 +4296,7 @@ sub git_feed {
}
if (defined $logo_url) {
# 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) {
# dummy date to keep the feed valid until commits trickle in:

View File

@ -96,7 +96,7 @@ static void flush(void)
if (output_fd >= 0)
write_or_die(output_fd, 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;
}
}

View File

@ -1222,7 +1222,7 @@ static int merge(struct commit *h1,
tree->object.parsed = 1;
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");
}

3
perl/.gitignore vendored
View File

@ -1,4 +1,5 @@
Makefile
perl.mak
perl.mak.old
blib
blibdirs
pm_to_blib

39
perl/Makefile Normal file
View File

@ -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

View File

@ -24,5 +24,6 @@ WriteMakefile(
NAME => 'Git',
VERSION_FROM => 'Git.pm',
PM => \%pm,
MAKEFILE => 'perl.mak',
%extra
);

View File

@ -11,7 +11,7 @@
static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
static int deny_non_fast_forwards = 0;
static int unpack_limit = 5000;
static int unpack_limit = 100;
static int report_status;
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);
}
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_list *bases, *ent;

View File

@ -109,12 +109,10 @@ index d99af23..8b32fb5 100644
+ whitespace at beginning
whitespace change
-whitespace in the middle
-whitespace at end
+white space in the middle
+whitespace at end
whitespace at end
unchanged line
-CR at endQ
+CR at end
CR at endQ
EOF
git-diff -b > out
test_expect_success 'another test, with -b' 'diff -u expect out'

70
t/t6024-recursive-merge.sh Executable file
View File

@ -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

View File

@ -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

View File

@ -142,4 +142,20 @@ test_expect_success \
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

View File

@ -370,7 +370,7 @@ int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
int i;
struct object_list *posn = trees;
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));
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;
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) {
posns = xmalloc(len * sizeof(struct tree_entry_list *));

View File

@ -230,7 +230,8 @@ unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
while (ptr + 1 < top && isspace(ptr[1])
&& ptr[1] != '\n')
ptr++;
if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
if (flags & XDF_IGNORE_WHITESPACE_CHANGE
&& ptr[1] != '\n') {
ha += (ha << 5);
ha ^= (unsigned long) ' ';
}