mirror of
https://github.com/git/git.git
synced 2026-04-01 20:40:08 +02:00
Merge branch 'master' of git://repo.or.cz/alt-git
This commit is contained in:
@@ -103,6 +103,10 @@ ifdef DOCBOOK_SUPPRESS_SP
|
||||
XMLTO_EXTRA += -m manpage-suppress-sp.xsl
|
||||
endif
|
||||
|
||||
SHELL_PATH ?= $(SHELL)
|
||||
# Shell quote;
|
||||
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
|
||||
|
||||
#
|
||||
# Please note that there is a minor bug in asciidoc.
|
||||
# The version after 6.0.3 _will_ include the patch found here:
|
||||
@@ -178,7 +182,7 @@ install-pdf: pdf
|
||||
$(INSTALL) -m 644 user-manual.pdf $(DESTDIR)$(pdfdir)
|
||||
|
||||
install-html: html
|
||||
sh ./install-webdoc.sh $(DESTDIR)$(htmldir)
|
||||
'$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir)
|
||||
|
||||
../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
|
||||
$(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) GIT-VERSION-FILE
|
||||
@@ -240,7 +244,7 @@ user-manual.xml: user-manual.txt user-manual.conf
|
||||
|
||||
technical/api-index.txt: technical/api-index-skel.txt \
|
||||
technical/api-index.sh $(patsubst %,%.txt,$(API_DOCS))
|
||||
$(QUIET_GEN)cd technical && sh ./api-index.sh
|
||||
$(QUIET_GEN)cd technical && '$(SHELL_PATH_SQ)' ./api-index.sh
|
||||
|
||||
$(patsubst %,%.html,$(API_DOCS) technical/api-index): %.html : %.txt
|
||||
$(QUIET_ASCIIDOC)$(ASCIIDOC) -b xhtml11 -f asciidoc.conf \
|
||||
@@ -285,7 +289,7 @@ $(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml
|
||||
|
||||
howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
|
||||
$(QUIET_GEN)$(RM) $@+ $@ && \
|
||||
sh ./howto-index.sh $(wildcard howto/*.txt) >$@+ && \
|
||||
'$(SHELL_PATH_SQ)' ./howto-index.sh $(wildcard howto/*.txt) >$@+ && \
|
||||
mv $@+ $@
|
||||
|
||||
$(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt
|
||||
@@ -299,14 +303,14 @@ $(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
|
||||
mv $@+ $@
|
||||
|
||||
install-webdoc : html
|
||||
sh ./install-webdoc.sh $(WEBDOC_DEST)
|
||||
'$(SHELL_PATH_SQ)' ./install-webdoc.sh $(WEBDOC_DEST)
|
||||
|
||||
quick-install: quick-install-man
|
||||
|
||||
quick-install-man:
|
||||
sh ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir)
|
||||
'$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir)
|
||||
|
||||
quick-install-html:
|
||||
sh ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir)
|
||||
'$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir)
|
||||
|
||||
.PHONY: .FORCE-GIT-VERSION-FILE
|
||||
|
||||
@@ -20,9 +20,3 @@ Fixes since v1.6.2.2
|
||||
to prevent them from being repacked.
|
||||
|
||||
Many small documentation updates are included as well.
|
||||
|
||||
---
|
||||
exec >/var/tmp/1
|
||||
echo O=$(git describe maint)
|
||||
O=v1.6.2.2-41-gbff82d0
|
||||
git shortlog --no-merges $O..maint
|
||||
|
||||
@@ -35,6 +35,8 @@ Updates since v1.6.2
|
||||
|
||||
(subsystems)
|
||||
|
||||
* various git-svn updates.
|
||||
|
||||
(performance)
|
||||
|
||||
* many uses of lstat(2) in the codepath for "git checkout" have been
|
||||
@@ -80,9 +82,12 @@ Updates since v1.6.2
|
||||
|
||||
* You can give --date=<format> option to git-blame.
|
||||
|
||||
* git-branch -r shows HEAD symref that points at a remote branch in
|
||||
* "git-branch -r" shows HEAD symref that points at a remote branch in
|
||||
interest of each tracked remote repository.
|
||||
|
||||
* "git-branch -v -v" is a new way to get list of names for branches and the
|
||||
"upstream" branch for them.
|
||||
|
||||
* git-config learned -e option to open an editor to edit the config file
|
||||
directly.
|
||||
|
||||
@@ -90,6 +95,8 @@ Updates since v1.6.2
|
||||
|
||||
* git-fast-export choked when seeing a tag that does not point at commit.
|
||||
|
||||
* git-for-each-ref learned a new "upstream" token.
|
||||
|
||||
* git-format-patch can be told to use attachment with a new configuration,
|
||||
format.attach.
|
||||
|
||||
@@ -118,6 +125,9 @@ Updates since v1.6.2
|
||||
|
||||
* Output from git-remote command has been vastly improved.
|
||||
|
||||
* "git remote update --prune $remote" updates from the named remote and
|
||||
then prunes stale tracking branches.
|
||||
|
||||
* git-send-email learned --confirm option to review the Cc: list before
|
||||
sending the messages out.
|
||||
|
||||
@@ -166,6 +176,6 @@ v1.6.2.X series.
|
||||
|
||||
---
|
||||
exec >/var/tmp/1
|
||||
O=v1.6.2.2-484-g796b137
|
||||
O=v1.6.2.3-497-g54a4749
|
||||
echo O=$(git describe master)
|
||||
git shortlog --no-merges $O..master ^maint
|
||||
|
||||
@@ -1215,7 +1215,7 @@ push.default::
|
||||
* `matching` push all matching branches.
|
||||
All branches having the same name in both ends are considered to be
|
||||
matching. This is the default.
|
||||
* `tracking` push the current branch to the branch it is tracking.
|
||||
* `tracking` push the current branch to its upstream branch.
|
||||
* `current` push the current branch to a branch of the same name.
|
||||
|
||||
rebase.stat::
|
||||
|
||||
@@ -100,7 +100,9 @@ OPTIONS
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Show sha1 and commit subject line for each head.
|
||||
Show sha1 and commit subject line for each head, along with
|
||||
relationship to upstream branch (if any). If given twice, print
|
||||
the name of the upstream branch, as well.
|
||||
|
||||
--abbrev=<length>::
|
||||
Alter the sha1's minimum display length in the output listing.
|
||||
|
||||
@@ -31,6 +31,9 @@ changes, which would normally have no effect. Nevertheless, this may be
|
||||
useful in the future for compensating for some git bugs or such,
|
||||
therefore such a usage is permitted.
|
||||
|
||||
*NOTE*: This command honors `.git/info/grafts`. If you have any grafts
|
||||
defined, running this command will make them permanent.
|
||||
|
||||
*WARNING*! The rewritten history will have different object names for all
|
||||
the objects and will not converge with the original branch. You will not
|
||||
be able to easily push and distribute the rewritten branch on top of the
|
||||
|
||||
@@ -85,6 +85,11 @@ objectsize::
|
||||
objectname::
|
||||
The object name (aka SHA-1).
|
||||
|
||||
upstream::
|
||||
The name of a local ref which can be considered ``upstream''
|
||||
from the displayed ref. Respects `:short` in the same way as
|
||||
`refname` above.
|
||||
|
||||
In addition to the above, for commit and tag objects, the header
|
||||
field names (`tree`, `parent`, `object`, `type`, and `tag`) can
|
||||
be used to specify the value in the header field.
|
||||
|
||||
@@ -16,7 +16,7 @@ SYNOPSIS
|
||||
'git remote set-head' <name> [-a | -d | <branch>]
|
||||
'git remote show' [-n] <name>
|
||||
'git remote prune' [-n | --dry-run] <name>
|
||||
'git remote update' [group]
|
||||
'git remote update' [-p | --prune] [group | remote]...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -125,6 +125,8 @@ the configuration parameter remotes.default will get used; if
|
||||
remotes.default is not defined, all remotes which do not have the
|
||||
configuration parameter remote.<name>.skipDefaultUpdate set to true will
|
||||
be updated. (See linkgit:git-config[1]).
|
||||
+
|
||||
With `--prune` option, prune all the remotes that are updated.
|
||||
|
||||
|
||||
DISCUSSION
|
||||
|
||||
@@ -85,6 +85,10 @@ COMMANDS
|
||||
specified, the prefix must include a trailing slash.
|
||||
Setting a prefix is useful if you wish to track multiple
|
||||
projects that share a common repository.
|
||||
--ignore-paths=<regex>;;
|
||||
When passed to 'init' or 'clone' this regular expression will
|
||||
be preserved as a config key. See 'fetch' for a description
|
||||
of '--ignore-paths'.
|
||||
|
||||
'fetch'::
|
||||
Fetch unfetched revisions from the Subversion remote we are
|
||||
@@ -97,6 +101,9 @@ COMMANDS
|
||||
makes 'git-log' (even without --date=local) show the same times
|
||||
that `svn log` would in the local timezone.
|
||||
|
||||
--parent;;
|
||||
Fetch only from the SVN parent of the current HEAD.
|
||||
|
||||
This doesn't interfere with interoperating with the Subversion
|
||||
repository you cloned from, but if you wish for your local Git
|
||||
repository to be able to interoperate with someone else's local Git
|
||||
@@ -104,17 +111,25 @@ repository, either don't use this option or you should both use it in
|
||||
the same local timezone.
|
||||
|
||||
--ignore-paths=<regex>;;
|
||||
This allows one to specify Perl regular expression that will
|
||||
This allows one to specify a Perl regular expression that will
|
||||
cause skipping of all matching paths from checkout from SVN.
|
||||
Examples:
|
||||
The '--ignore-paths' option should match for every 'fetch'
|
||||
(including automatic fetches due to 'clone', 'dcommit',
|
||||
'rebase', etc) on a given repository.
|
||||
|
||||
--ignore-paths="^doc" - skip "doc*" directory for every fetch.
|
||||
config key: svn-remote.<name>.ignore-paths
|
||||
|
||||
--ignore-paths="^[^/]+/(?:branches|tags)" - skip "branches"
|
||||
and "tags" of first level directories.
|
||||
If the ignore-paths config key is set and the command
|
||||
line option is also given, both regular expressions
|
||||
will be used.
|
||||
|
||||
Regular expression is not persistent, you should specify
|
||||
it every time when fetching.
|
||||
Examples:
|
||||
|
||||
--ignore-paths="^doc" - skip "doc*" directory for every
|
||||
fetch.
|
||||
|
||||
--ignore-paths="^[^/]+/(?:branches|tags)" - skip
|
||||
"branches" and "tags" of first level directories.
|
||||
|
||||
'clone'::
|
||||
Runs 'init' and 'fetch'. It will automatically create a
|
||||
|
||||
@@ -43,9 +43,11 @@ unreleased) version of git, that is available from 'master'
|
||||
branch of the `git.git` repository.
|
||||
Documentation for older releases are available here:
|
||||
|
||||
* link:v1.6.2.1/git.html[documentation for release 1.6.2.1]
|
||||
* link:v1.6.2.3/git.html[documentation for release 1.6.2.3]
|
||||
|
||||
* release notes for
|
||||
link:RelNotes-1.6.2.3.txt[1.6.2.3],
|
||||
link:RelNotes-1.6.2.2.txt[1.6.2.2],
|
||||
link:RelNotes-1.6.2.1.txt[1.6.2.1],
|
||||
link:RelNotes-1.6.2.txt[1.6.2].
|
||||
|
||||
|
||||
@@ -449,6 +449,12 @@ This commit is referred to as a "merge commit", or sometimes just a
|
||||
An <<def_object,object>> which is not <<def_reachable,reachable>> from a
|
||||
<<def_branch,branch>>, <<def_tag,tag>>, or any other reference.
|
||||
|
||||
[[def_upstream_branch]]upstream branch::
|
||||
The default <<def_branch,branch>> that is merged into the branch in
|
||||
question (or the branch in question is rebased onto). It is configured
|
||||
via branch.<name>.remote and branch.<name>.merge. If the upstream branch
|
||||
of 'A' is 'origin/B' sometimes we say "'A' is tracking 'origin/B'".
|
||||
|
||||
[[def_working_tree]]working tree::
|
||||
The tree of actual checked out files. The working tree is
|
||||
normally equal to the <<def_HEAD,HEAD>> plus any local changes
|
||||
|
||||
30
Makefile
30
Makefile
@@ -145,6 +145,8 @@ all::
|
||||
# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
|
||||
# MakeMaker (e.g. using ActiveState under Cygwin).
|
||||
#
|
||||
# Define NO_PERL if you do not want Perl scripts or libraries at all.
|
||||
#
|
||||
# Define NO_TCLTK if you do not want Tcl/Tk GUI.
|
||||
#
|
||||
# The TCL_PATH variable governs the location of the Tcl interpreter
|
||||
@@ -354,7 +356,10 @@ BUILT_INS += git-whatchanged$X
|
||||
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
|
||||
|
||||
# what 'all' will build but not install in gitexecdir
|
||||
OTHER_PROGRAMS = git$X gitweb/gitweb.cgi
|
||||
OTHER_PROGRAMS = git$X
|
||||
ifndef NO_PERL
|
||||
OTHER_PROGRAMS += gitweb/gitweb.cgi
|
||||
endif
|
||||
|
||||
# Set paths to tools early so that they can be used for version tests.
|
||||
ifndef SHELL_PATH
|
||||
@@ -433,6 +438,7 @@ LIB_OBJS += archive-tar.o
|
||||
LIB_OBJS += archive-zip.o
|
||||
LIB_OBJS += attr.o
|
||||
LIB_OBJS += base85.o
|
||||
LIB_OBJS += bisect.o
|
||||
LIB_OBJS += blob.o
|
||||
LIB_OBJS += branch.o
|
||||
LIB_OBJS += bundle.o
|
||||
@@ -533,6 +539,7 @@ BUILTIN_OBJS += builtin-add.o
|
||||
BUILTIN_OBJS += builtin-annotate.o
|
||||
BUILTIN_OBJS += builtin-apply.o
|
||||
BUILTIN_OBJS += builtin-archive.o
|
||||
BUILTIN_OBJS += builtin-bisect--helper.o
|
||||
BUILTIN_OBJS += builtin-blame.o
|
||||
BUILTIN_OBJS += builtin-branch.o
|
||||
BUILTIN_OBJS += builtin-bundle.o
|
||||
@@ -1106,6 +1113,10 @@ ifeq ($(TCLTK_PATH),)
|
||||
NO_TCLTK=NoThanks
|
||||
endif
|
||||
|
||||
ifeq ($(PERL_PATH),)
|
||||
NO_PERL=NoThanks
|
||||
endif
|
||||
|
||||
QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
|
||||
QUIET_SUBDIR1 =
|
||||
|
||||
@@ -1180,7 +1191,9 @@ ifndef NO_TCLTK
|
||||
$(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) gitexecdir='$(gitexec_instdir_SQ)' all
|
||||
$(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all
|
||||
endif
|
||||
ifndef NO_PERL
|
||||
$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
|
||||
endif
|
||||
$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1)
|
||||
|
||||
please_set_SHELL_PATH_to_a_more_modern_shell:
|
||||
@@ -1228,6 +1241,7 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
|
||||
chmod +x $@+ && \
|
||||
mv $@+ $@
|
||||
|
||||
ifndef NO_PERL
|
||||
$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
|
||||
|
||||
perl/perl.mak: GIT-CFLAGS perl/Makefile perl/Makefile.PL
|
||||
@@ -1287,6 +1301,15 @@ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
|
||||
$@.sh > $@+ && \
|
||||
chmod +x $@+ && \
|
||||
mv $@+ $@
|
||||
else # NO_PERL
|
||||
$(patsubst %.perl,%,$(SCRIPT_PERL)) git-instaweb: % : unimplemented.sh
|
||||
$(QUIET_GEN)$(RM) $@ $@+ && \
|
||||
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
|
||||
-e 's|@@REASON@@|NO_PERL=$(NO_PERL)|g' \
|
||||
unimplemented.sh >$@+ && \
|
||||
chmod +x $@+ && \
|
||||
mv $@+ $@
|
||||
endif # NO_PERL
|
||||
|
||||
configure: configure.ac
|
||||
$(QUIET_GEN)$(RM) $@ $<+ && \
|
||||
@@ -1403,6 +1426,7 @@ GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
|
||||
@echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
|
||||
@echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
|
||||
@echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
|
||||
@echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
|
||||
|
||||
### Detect Tck/Tk interpreter path changes
|
||||
ifndef NO_TCLTK
|
||||
@@ -1605,9 +1629,11 @@ clean:
|
||||
$(RM) -r $(GIT_TARNAME) .doc-tmp-dir
|
||||
$(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
|
||||
$(RM) $(htmldocs).tar.gz $(manpages).tar.gz
|
||||
$(RM) gitweb/gitweb.cgi
|
||||
$(MAKE) -C Documentation/ clean
|
||||
ifndef NO_PERL
|
||||
$(RM) gitweb/gitweb.cgi
|
||||
$(MAKE) -C perl clean
|
||||
endif
|
||||
$(MAKE) -C templates/ clean
|
||||
$(MAKE) -C t/ clean
|
||||
ifndef NO_TCLTK
|
||||
|
||||
556
bisect.c
Normal file
556
bisect.c
Normal file
@@ -0,0 +1,556 @@
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
#include "refs.h"
|
||||
#include "list-objects.h"
|
||||
#include "quote.h"
|
||||
#include "sha1-lookup.h"
|
||||
#include "bisect.h"
|
||||
|
||||
static unsigned char (*skipped_sha1)[20];
|
||||
static int skipped_sha1_nr;
|
||||
static int skipped_sha1_alloc;
|
||||
|
||||
static const char **rev_argv;
|
||||
static int rev_argv_nr;
|
||||
static int rev_argv_alloc;
|
||||
|
||||
/* bits #0-15 in revision.h */
|
||||
|
||||
#define COUNTED (1u<<16)
|
||||
|
||||
/*
|
||||
* This is a truly stupid algorithm, but it's only
|
||||
* used for bisection, and we just don't care enough.
|
||||
*
|
||||
* We care just barely enough to avoid recursing for
|
||||
* non-merge entries.
|
||||
*/
|
||||
static int count_distance(struct commit_list *entry)
|
||||
{
|
||||
int nr = 0;
|
||||
|
||||
while (entry) {
|
||||
struct commit *commit = entry->item;
|
||||
struct commit_list *p;
|
||||
|
||||
if (commit->object.flags & (UNINTERESTING | COUNTED))
|
||||
break;
|
||||
if (!(commit->object.flags & TREESAME))
|
||||
nr++;
|
||||
commit->object.flags |= COUNTED;
|
||||
p = commit->parents;
|
||||
entry = p;
|
||||
if (p) {
|
||||
p = p->next;
|
||||
while (p) {
|
||||
nr += count_distance(p);
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nr;
|
||||
}
|
||||
|
||||
static void clear_distance(struct commit_list *list)
|
||||
{
|
||||
while (list) {
|
||||
struct commit *commit = list->item;
|
||||
commit->object.flags &= ~COUNTED;
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
#define DEBUG_BISECT 0
|
||||
|
||||
static inline int weight(struct commit_list *elem)
|
||||
{
|
||||
return *((int*)(elem->item->util));
|
||||
}
|
||||
|
||||
static inline void weight_set(struct commit_list *elem, int weight)
|
||||
{
|
||||
*((int*)(elem->item->util)) = weight;
|
||||
}
|
||||
|
||||
static int count_interesting_parents(struct commit *commit)
|
||||
{
|
||||
struct commit_list *p;
|
||||
int count;
|
||||
|
||||
for (count = 0, p = commit->parents; p; p = p->next) {
|
||||
if (p->item->object.flags & UNINTERESTING)
|
||||
continue;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static inline int halfway(struct commit_list *p, int nr)
|
||||
{
|
||||
/*
|
||||
* Don't short-cut something we are not going to return!
|
||||
*/
|
||||
if (p->item->object.flags & TREESAME)
|
||||
return 0;
|
||||
if (DEBUG_BISECT)
|
||||
return 0;
|
||||
/*
|
||||
* 2 and 3 are halfway of 5.
|
||||
* 3 is halfway of 6 but 2 and 4 are not.
|
||||
*/
|
||||
switch (2 * weight(p) - nr) {
|
||||
case -1: case 0: case 1:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if !DEBUG_BISECT
|
||||
#define show_list(a,b,c,d) do { ; } while (0)
|
||||
#else
|
||||
static void show_list(const char *debug, int counted, int nr,
|
||||
struct commit_list *list)
|
||||
{
|
||||
struct commit_list *p;
|
||||
|
||||
fprintf(stderr, "%s (%d/%d)\n", debug, counted, nr);
|
||||
|
||||
for (p = list; p; p = p->next) {
|
||||
struct commit_list *pp;
|
||||
struct commit *commit = p->item;
|
||||
unsigned flags = commit->object.flags;
|
||||
enum object_type type;
|
||||
unsigned long size;
|
||||
char *buf = read_sha1_file(commit->object.sha1, &type, &size);
|
||||
char *ep, *sp;
|
||||
|
||||
fprintf(stderr, "%c%c%c ",
|
||||
(flags & TREESAME) ? ' ' : 'T',
|
||||
(flags & UNINTERESTING) ? 'U' : ' ',
|
||||
(flags & COUNTED) ? 'C' : ' ');
|
||||
if (commit->util)
|
||||
fprintf(stderr, "%3d", weight(p));
|
||||
else
|
||||
fprintf(stderr, "---");
|
||||
fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.sha1));
|
||||
for (pp = commit->parents; pp; pp = pp->next)
|
||||
fprintf(stderr, " %.*s", 8,
|
||||
sha1_to_hex(pp->item->object.sha1));
|
||||
|
||||
sp = strstr(buf, "\n\n");
|
||||
if (sp) {
|
||||
sp += 2;
|
||||
for (ep = sp; *ep && *ep != '\n'; ep++)
|
||||
;
|
||||
fprintf(stderr, " %.*s", (int)(ep - sp), sp);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
#endif /* DEBUG_BISECT */
|
||||
|
||||
static struct commit_list *best_bisection(struct commit_list *list, int nr)
|
||||
{
|
||||
struct commit_list *p, *best;
|
||||
int best_distance = -1;
|
||||
|
||||
best = list;
|
||||
for (p = list; p; p = p->next) {
|
||||
int distance;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (flags & TREESAME)
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
distance = nr - distance;
|
||||
if (distance > best_distance) {
|
||||
best = p;
|
||||
best_distance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
struct commit_dist {
|
||||
struct commit *commit;
|
||||
int distance;
|
||||
};
|
||||
|
||||
static int compare_commit_dist(const void *a_, const void *b_)
|
||||
{
|
||||
struct commit_dist *a, *b;
|
||||
|
||||
a = (struct commit_dist *)a_;
|
||||
b = (struct commit_dist *)b_;
|
||||
if (a->distance != b->distance)
|
||||
return b->distance - a->distance; /* desc sort */
|
||||
return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
|
||||
}
|
||||
|
||||
static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
|
||||
{
|
||||
struct commit_list *p;
|
||||
struct commit_dist *array = xcalloc(nr, sizeof(*array));
|
||||
int cnt, i;
|
||||
|
||||
for (p = list, cnt = 0; p; p = p->next) {
|
||||
int distance;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (flags & TREESAME)
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
distance = nr - distance;
|
||||
array[cnt].commit = p->item;
|
||||
array[cnt].distance = distance;
|
||||
cnt++;
|
||||
}
|
||||
qsort(array, cnt, sizeof(*array), compare_commit_dist);
|
||||
for (p = list, i = 0; i < cnt; i++) {
|
||||
struct name_decoration *r = xmalloc(sizeof(*r) + 100);
|
||||
struct object *obj = &(array[i].commit->object);
|
||||
|
||||
sprintf(r->name, "dist=%d", array[i].distance);
|
||||
r->next = add_decoration(&name_decoration, obj, r);
|
||||
p->item = array[i].commit;
|
||||
p = p->next;
|
||||
}
|
||||
if (p)
|
||||
p->next = NULL;
|
||||
free(array);
|
||||
return list;
|
||||
}
|
||||
|
||||
/*
|
||||
* zero or positive weight is the number of interesting commits it can
|
||||
* reach, including itself. Especially, weight = 0 means it does not
|
||||
* reach any tree-changing commits (e.g. just above uninteresting one
|
||||
* but traversal is with pathspec).
|
||||
*
|
||||
* weight = -1 means it has one parent and its distance is yet to
|
||||
* be computed.
|
||||
*
|
||||
* weight = -2 means it has more than one parent and its distance is
|
||||
* unknown. After running count_distance() first, they will get zero
|
||||
* or positive distance.
|
||||
*/
|
||||
static struct commit_list *do_find_bisection(struct commit_list *list,
|
||||
int nr, int *weights,
|
||||
int find_all)
|
||||
{
|
||||
int n, counted;
|
||||
struct commit_list *p;
|
||||
|
||||
counted = 0;
|
||||
|
||||
for (n = 0, p = list; p; p = p->next) {
|
||||
struct commit *commit = p->item;
|
||||
unsigned flags = commit->object.flags;
|
||||
|
||||
p->item->util = &weights[n++];
|
||||
switch (count_interesting_parents(commit)) {
|
||||
case 0:
|
||||
if (!(flags & TREESAME)) {
|
||||
weight_set(p, 1);
|
||||
counted++;
|
||||
show_list("bisection 2 count one",
|
||||
counted, nr, list);
|
||||
}
|
||||
/*
|
||||
* otherwise, it is known not to reach any
|
||||
* tree-changing commit and gets weight 0.
|
||||
*/
|
||||
break;
|
||||
case 1:
|
||||
weight_set(p, -1);
|
||||
break;
|
||||
default:
|
||||
weight_set(p, -2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
show_list("bisection 2 initialize", counted, nr, list);
|
||||
|
||||
/*
|
||||
* If you have only one parent in the resulting set
|
||||
* then you can reach one commit more than that parent
|
||||
* can reach. So we do not have to run the expensive
|
||||
* count_distance() for single strand of pearls.
|
||||
*
|
||||
* However, if you have more than one parents, you cannot
|
||||
* just add their distance and one for yourself, since
|
||||
* they usually reach the same ancestor and you would
|
||||
* end up counting them twice that way.
|
||||
*
|
||||
* So we will first count distance of merges the usual
|
||||
* way, and then fill the blanks using cheaper algorithm.
|
||||
*/
|
||||
for (p = list; p; p = p->next) {
|
||||
if (p->item->object.flags & UNINTERESTING)
|
||||
continue;
|
||||
if (weight(p) != -2)
|
||||
continue;
|
||||
weight_set(p, count_distance(p));
|
||||
clear_distance(list);
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
if (!find_all && halfway(p, nr))
|
||||
return p;
|
||||
counted++;
|
||||
}
|
||||
|
||||
show_list("bisection 2 count_distance", counted, nr, list);
|
||||
|
||||
while (counted < nr) {
|
||||
for (p = list; p; p = p->next) {
|
||||
struct commit_list *q;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (0 <= weight(p))
|
||||
continue;
|
||||
for (q = p->item->parents; q; q = q->next) {
|
||||
if (q->item->object.flags & UNINTERESTING)
|
||||
continue;
|
||||
if (0 <= weight(q))
|
||||
break;
|
||||
}
|
||||
if (!q)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* weight for p is unknown but q is known.
|
||||
* add one for p itself if p is to be counted,
|
||||
* otherwise inherit it from q directly.
|
||||
*/
|
||||
if (!(flags & TREESAME)) {
|
||||
weight_set(p, weight(q)+1);
|
||||
counted++;
|
||||
show_list("bisection 2 count one",
|
||||
counted, nr, list);
|
||||
}
|
||||
else
|
||||
weight_set(p, weight(q));
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
if (!find_all && halfway(p, nr))
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
show_list("bisection 2 counted all", counted, nr, list);
|
||||
|
||||
if (!find_all)
|
||||
return best_bisection(list, nr);
|
||||
else
|
||||
return best_bisection_sorted(list, nr);
|
||||
}
|
||||
|
||||
struct commit_list *find_bisection(struct commit_list *list,
|
||||
int *reaches, int *all,
|
||||
int find_all)
|
||||
{
|
||||
int nr, on_list;
|
||||
struct commit_list *p, *best, *next, *last;
|
||||
int *weights;
|
||||
|
||||
show_list("bisection 2 entry", 0, 0, list);
|
||||
|
||||
/*
|
||||
* Count the number of total and tree-changing items on the
|
||||
* list, while reversing the list.
|
||||
*/
|
||||
for (nr = on_list = 0, last = NULL, p = list;
|
||||
p;
|
||||
p = next) {
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
next = p->next;
|
||||
if (flags & UNINTERESTING)
|
||||
continue;
|
||||
p->next = last;
|
||||
last = p;
|
||||
if (!(flags & TREESAME))
|
||||
nr++;
|
||||
on_list++;
|
||||
}
|
||||
list = last;
|
||||
show_list("bisection 2 sorted", 0, nr, list);
|
||||
|
||||
*all = nr;
|
||||
weights = xcalloc(on_list, sizeof(*weights));
|
||||
|
||||
/* Do the real work of finding bisection commit. */
|
||||
best = do_find_bisection(list, nr, weights, find_all);
|
||||
if (best) {
|
||||
if (!find_all)
|
||||
best->next = NULL;
|
||||
*reaches = weight(best);
|
||||
}
|
||||
free(weights);
|
||||
return best;
|
||||
}
|
||||
|
||||
static int register_ref(const char *refname, const unsigned char *sha1,
|
||||
int flags, void *cb_data)
|
||||
{
|
||||
if (!strcmp(refname, "bad")) {
|
||||
ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
|
||||
rev_argv[rev_argv_nr++] = xstrdup(sha1_to_hex(sha1));
|
||||
} else if (!prefixcmp(refname, "good-")) {
|
||||
const char *hex = sha1_to_hex(sha1);
|
||||
char *good = xmalloc(strlen(hex) + 2);
|
||||
*good = '^';
|
||||
memcpy(good + 1, hex, strlen(hex) + 1);
|
||||
ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
|
||||
rev_argv[rev_argv_nr++] = good;
|
||||
} else if (!prefixcmp(refname, "skip-")) {
|
||||
ALLOC_GROW(skipped_sha1, skipped_sha1_nr + 1,
|
||||
skipped_sha1_alloc);
|
||||
hashcpy(skipped_sha1[skipped_sha1_nr++], sha1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_bisect_refs(void)
|
||||
{
|
||||
return for_each_ref_in("refs/bisect/", register_ref, NULL);
|
||||
}
|
||||
|
||||
void read_bisect_paths(void)
|
||||
{
|
||||
struct strbuf str = STRBUF_INIT;
|
||||
const char *filename = git_path("BISECT_NAMES");
|
||||
FILE *fp = fopen(filename, "r");
|
||||
|
||||
if (!fp)
|
||||
die("Could not open file '%s': %s", filename, strerror(errno));
|
||||
|
||||
while (strbuf_getline(&str, fp, '\n') != EOF) {
|
||||
char *quoted;
|
||||
int res;
|
||||
|
||||
strbuf_trim(&str);
|
||||
quoted = strbuf_detach(&str, NULL);
|
||||
res = sq_dequote_to_argv(quoted, &rev_argv,
|
||||
&rev_argv_nr, &rev_argv_alloc);
|
||||
if (res)
|
||||
die("Badly quoted content in file '%s': %s",
|
||||
filename, quoted);
|
||||
}
|
||||
|
||||
strbuf_release(&str);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static int skipcmp(const void *a, const void *b)
|
||||
{
|
||||
return hashcmp(a, b);
|
||||
}
|
||||
|
||||
static void prepare_skipped(void)
|
||||
{
|
||||
qsort(skipped_sha1, skipped_sha1_nr, sizeof(*skipped_sha1), skipcmp);
|
||||
}
|
||||
|
||||
static const unsigned char *skipped_sha1_access(size_t index, void *table)
|
||||
{
|
||||
unsigned char (*skipped)[20] = table;
|
||||
return skipped[index];
|
||||
}
|
||||
|
||||
static int lookup_skipped(unsigned char *sha1)
|
||||
{
|
||||
return sha1_pos(sha1, skipped_sha1, skipped_sha1_nr,
|
||||
skipped_sha1_access);
|
||||
}
|
||||
|
||||
struct commit_list *filter_skipped(struct commit_list *list,
|
||||
struct commit_list **tried,
|
||||
int show_all)
|
||||
{
|
||||
struct commit_list *filtered = NULL, **f = &filtered;
|
||||
|
||||
*tried = NULL;
|
||||
|
||||
if (!skipped_sha1_nr)
|
||||
return list;
|
||||
|
||||
prepare_skipped();
|
||||
|
||||
while (list) {
|
||||
struct commit_list *next = list->next;
|
||||
list->next = NULL;
|
||||
if (0 <= lookup_skipped(list->item->object.sha1)) {
|
||||
/* Move current to tried list */
|
||||
*tried = list;
|
||||
tried = &list->next;
|
||||
} else {
|
||||
if (!show_all)
|
||||
return list;
|
||||
/* Move current to filtered list */
|
||||
*f = list;
|
||||
f = &list->next;
|
||||
}
|
||||
list = next;
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
static void bisect_rev_setup(struct rev_info *revs, const char *prefix)
|
||||
{
|
||||
init_revisions(revs, prefix);
|
||||
revs->abbrev = 0;
|
||||
revs->commit_format = CMIT_FMT_UNSPECIFIED;
|
||||
|
||||
/* argv[0] will be ignored by setup_revisions */
|
||||
ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
|
||||
rev_argv[rev_argv_nr++] = xstrdup("bisect_rev_setup");
|
||||
|
||||
if (read_bisect_refs())
|
||||
die("reading bisect refs failed");
|
||||
|
||||
ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
|
||||
rev_argv[rev_argv_nr++] = xstrdup("--");
|
||||
|
||||
read_bisect_paths();
|
||||
|
||||
ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
|
||||
rev_argv[rev_argv_nr++] = NULL;
|
||||
|
||||
setup_revisions(rev_argv_nr, rev_argv, revs, NULL);
|
||||
|
||||
revs->limited = 1;
|
||||
}
|
||||
|
||||
int bisect_next_vars(const char *prefix)
|
||||
{
|
||||
struct rev_info revs;
|
||||
struct rev_list_info info;
|
||||
int reaches = 0, all = 0;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.revs = &revs;
|
||||
info.bisect_show_flags = BISECT_SHOW_TRIED | BISECT_SHOW_STRINGED;
|
||||
|
||||
bisect_rev_setup(&revs, prefix);
|
||||
|
||||
if (prepare_revision_walk(&revs))
|
||||
die("revision walk setup failed");
|
||||
if (revs.tree_objects)
|
||||
mark_edges_uninteresting(revs.commits, &revs, NULL);
|
||||
|
||||
revs.commits = find_bisection(revs.commits, &reaches, &all,
|
||||
!!skipped_sha1_nr);
|
||||
|
||||
return show_bisect_vars(&info, reaches, all);
|
||||
}
|
||||
29
bisect.h
Normal file
29
bisect.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef BISECT_H
|
||||
#define BISECT_H
|
||||
|
||||
extern struct commit_list *find_bisection(struct commit_list *list,
|
||||
int *reaches, int *all,
|
||||
int find_all);
|
||||
|
||||
extern struct commit_list *filter_skipped(struct commit_list *list,
|
||||
struct commit_list **tried,
|
||||
int show_all);
|
||||
|
||||
/* bisect_show_flags flags in struct rev_list_info */
|
||||
#define BISECT_SHOW_ALL (1<<0)
|
||||
#define BISECT_SHOW_TRIED (1<<1)
|
||||
#define BISECT_SHOW_STRINGED (1<<2)
|
||||
|
||||
struct rev_list_info {
|
||||
struct rev_info *revs;
|
||||
int bisect_show_flags;
|
||||
int show_timestamp;
|
||||
int hdr_termination;
|
||||
const char *header_prefix;
|
||||
};
|
||||
|
||||
extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all);
|
||||
|
||||
extern int bisect_next_vars(const char *prefix);
|
||||
|
||||
#endif
|
||||
27
builtin-bisect--helper.c
Normal file
27
builtin-bisect--helper.c
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "builtin.h"
|
||||
#include "cache.h"
|
||||
#include "parse-options.h"
|
||||
#include "bisect.h"
|
||||
|
||||
static const char * const git_bisect_helper_usage[] = {
|
||||
"git bisect--helper --next-vars",
|
||||
NULL
|
||||
};
|
||||
|
||||
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int next_vars = 0;
|
||||
struct option options[] = {
|
||||
OPT_BOOLEAN(0, "next-vars", &next_vars,
|
||||
"output next bisect step variables"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, git_bisect_helper_usage, 0);
|
||||
|
||||
if (!next_vars)
|
||||
usage_with_options(git_bisect_helper_usage, options);
|
||||
|
||||
/* next-vars */
|
||||
return bisect_next_vars(prefix);
|
||||
}
|
||||
@@ -301,19 +301,30 @@ static int ref_cmp(const void *r1, const void *r2)
|
||||
return strcmp(c1->name, c2->name);
|
||||
}
|
||||
|
||||
static void fill_tracking_info(struct strbuf *stat, const char *branch_name)
|
||||
static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
|
||||
int show_upstream_ref)
|
||||
{
|
||||
int ours, theirs;
|
||||
struct branch *branch = branch_get(branch_name);
|
||||
|
||||
if (!stat_tracking_info(branch, &ours, &theirs) || (!ours && !theirs))
|
||||
if (!stat_tracking_info(branch, &ours, &theirs)) {
|
||||
if (branch && branch->merge && branch->merge[0]->dst &&
|
||||
show_upstream_ref)
|
||||
strbuf_addf(stat, "[%s] ",
|
||||
shorten_unambiguous_ref(branch->merge[0]->dst));
|
||||
return;
|
||||
}
|
||||
|
||||
strbuf_addch(stat, '[');
|
||||
if (show_upstream_ref)
|
||||
strbuf_addf(stat, "%s: ",
|
||||
shorten_unambiguous_ref(branch->merge[0]->dst));
|
||||
if (!ours)
|
||||
strbuf_addf(stat, "[behind %d] ", theirs);
|
||||
strbuf_addf(stat, "behind %d] ", theirs);
|
||||
else if (!theirs)
|
||||
strbuf_addf(stat, "[ahead %d] ", ours);
|
||||
strbuf_addf(stat, "ahead %d] ", ours);
|
||||
else
|
||||
strbuf_addf(stat, "[ahead %d, behind %d] ", ours, theirs);
|
||||
strbuf_addf(stat, "ahead %d, behind %d] ", ours, theirs);
|
||||
}
|
||||
|
||||
static int matches_merge_filter(struct commit *commit)
|
||||
@@ -379,7 +390,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
|
||||
}
|
||||
|
||||
if (item->kind == REF_LOCAL_BRANCH)
|
||||
fill_tracking_info(&stat, item->name);
|
||||
fill_tracking_info(&stat, item->name, verbose > 1);
|
||||
|
||||
strbuf_addf(&out, " %s %s%s",
|
||||
find_unique_abbrev(item->commit->object.sha1, abbrev),
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "blob.h"
|
||||
#include "quote.h"
|
||||
#include "parse-options.h"
|
||||
#include "remote.h"
|
||||
|
||||
/* Quoting styles */
|
||||
#define QUOTE_NONE 0
|
||||
@@ -66,6 +67,7 @@ static struct {
|
||||
{ "subject" },
|
||||
{ "body" },
|
||||
{ "contents" },
|
||||
{ "upstream" },
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -543,109 +545,6 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* generate a format suitable for scanf from a ref_rev_parse_rules
|
||||
* rule, that is replace the "%.*s" spec with a "%s" spec
|
||||
*/
|
||||
static void gen_scanf_fmt(char *scanf_fmt, const char *rule)
|
||||
{
|
||||
char *spec;
|
||||
|
||||
spec = strstr(rule, "%.*s");
|
||||
if (!spec || strstr(spec + 4, "%.*s"))
|
||||
die("invalid rule in ref_rev_parse_rules: %s", rule);
|
||||
|
||||
/* copy all until spec */
|
||||
strncpy(scanf_fmt, rule, spec - rule);
|
||||
scanf_fmt[spec - rule] = '\0';
|
||||
/* copy new spec */
|
||||
strcat(scanf_fmt, "%s");
|
||||
/* copy remaining rule */
|
||||
strcat(scanf_fmt, spec + 4);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shorten the refname to an non-ambiguous form
|
||||
*/
|
||||
static char *get_short_ref(struct refinfo *ref)
|
||||
{
|
||||
int i;
|
||||
static char **scanf_fmts;
|
||||
static int nr_rules;
|
||||
char *short_name;
|
||||
|
||||
/* pre generate scanf formats from ref_rev_parse_rules[] */
|
||||
if (!nr_rules) {
|
||||
size_t total_len = 0;
|
||||
|
||||
/* the rule list is NULL terminated, count them first */
|
||||
for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
|
||||
/* no +1 because strlen("%s") < strlen("%.*s") */
|
||||
total_len += strlen(ref_rev_parse_rules[nr_rules]);
|
||||
|
||||
scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
|
||||
|
||||
total_len = 0;
|
||||
for (i = 0; i < nr_rules; i++) {
|
||||
scanf_fmts[i] = (char *)&scanf_fmts[nr_rules]
|
||||
+ total_len;
|
||||
gen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);
|
||||
total_len += strlen(ref_rev_parse_rules[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* bail out if there are no rules */
|
||||
if (!nr_rules)
|
||||
return ref->refname;
|
||||
|
||||
/* buffer for scanf result, at most ref->refname must fit */
|
||||
short_name = xstrdup(ref->refname);
|
||||
|
||||
/* skip first rule, it will always match */
|
||||
for (i = nr_rules - 1; i > 0 ; --i) {
|
||||
int j;
|
||||
int short_name_len;
|
||||
|
||||
if (1 != sscanf(ref->refname, scanf_fmts[i], short_name))
|
||||
continue;
|
||||
|
||||
short_name_len = strlen(short_name);
|
||||
|
||||
/*
|
||||
* check if the short name resolves to a valid ref,
|
||||
* but use only rules prior to the matched one
|
||||
*/
|
||||
for (j = 0; j < i; j++) {
|
||||
const char *rule = ref_rev_parse_rules[j];
|
||||
unsigned char short_objectname[20];
|
||||
char refname[PATH_MAX];
|
||||
|
||||
/*
|
||||
* the short name is ambiguous, if it resolves
|
||||
* (with this previous rule) to a valid ref
|
||||
* read_ref() returns 0 on success
|
||||
*/
|
||||
mksnpath(refname, sizeof(refname),
|
||||
rule, short_name_len, short_name);
|
||||
if (!read_ref(refname, short_objectname))
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* short name is non-ambiguous if all previous rules
|
||||
* haven't resolved to a valid ref
|
||||
*/
|
||||
if (j == i)
|
||||
return short_name;
|
||||
}
|
||||
|
||||
free(short_name);
|
||||
return ref->refname;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse the object referred by ref, and grab needed value.
|
||||
*/
|
||||
@@ -672,32 +571,49 @@ static void populate_value(struct refinfo *ref)
|
||||
const char *name = used_atom[i];
|
||||
struct atom_value *v = &ref->value[i];
|
||||
int deref = 0;
|
||||
const char *refname;
|
||||
const char *formatp;
|
||||
|
||||
if (*name == '*') {
|
||||
deref = 1;
|
||||
name++;
|
||||
}
|
||||
if (!prefixcmp(name, "refname")) {
|
||||
const char *formatp = strchr(name, ':');
|
||||
const char *refname = ref->refname;
|
||||
|
||||
/* look for "short" refname format */
|
||||
if (formatp) {
|
||||
formatp++;
|
||||
if (!strcmp(formatp, "short"))
|
||||
refname = get_short_ref(ref);
|
||||
else
|
||||
die("unknown refname format %s",
|
||||
formatp);
|
||||
}
|
||||
if (!prefixcmp(name, "refname"))
|
||||
refname = ref->refname;
|
||||
else if(!prefixcmp(name, "upstream")) {
|
||||
struct branch *branch;
|
||||
/* only local branches may have an upstream */
|
||||
if (prefixcmp(ref->refname, "refs/heads/"))
|
||||
continue;
|
||||
branch = branch_get(ref->refname + 11);
|
||||
|
||||
if (!deref)
|
||||
v->s = refname;
|
||||
else {
|
||||
int len = strlen(refname);
|
||||
char *s = xmalloc(len + 4);
|
||||
sprintf(s, "%s^{}", refname);
|
||||
v->s = s;
|
||||
}
|
||||
if (!branch || !branch->merge || !branch->merge[0] ||
|
||||
!branch->merge[0]->dst)
|
||||
continue;
|
||||
refname = branch->merge[0]->dst;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
formatp = strchr(name, ':');
|
||||
/* look for "short" refname format */
|
||||
if (formatp) {
|
||||
formatp++;
|
||||
if (!strcmp(formatp, "short"))
|
||||
refname = shorten_unambiguous_ref(refname);
|
||||
else
|
||||
die("unknown %.*s format %s",
|
||||
(int)(formatp - name), name, formatp);
|
||||
}
|
||||
|
||||
if (!deref)
|
||||
v->s = refname;
|
||||
else {
|
||||
int len = strlen(refname);
|
||||
char *s = xmalloc(len + 4);
|
||||
sprintf(s, "%s^{}", refname);
|
||||
v->s = s;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1612,7 +1612,7 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
|
||||
return;
|
||||
}
|
||||
if (progress > pack_to_stdout)
|
||||
fprintf(stderr, "Delta compression using %d threads.\n",
|
||||
fprintf(stderr, "Delta compression using up to %d threads.\n",
|
||||
delta_search_threads);
|
||||
|
||||
/* Partition the work amongst work threads. */
|
||||
@@ -1901,17 +1901,19 @@ static void read_object_list_from_stdin(void)
|
||||
|
||||
#define OBJECT_ADDED (1u<<20)
|
||||
|
||||
static void show_commit(struct commit *commit)
|
||||
static void show_commit(struct commit *commit, void *data)
|
||||
{
|
||||
add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
|
||||
commit->object.flags |= OBJECT_ADDED;
|
||||
}
|
||||
|
||||
static void show_object(struct object_array_entry *p)
|
||||
static void show_object(struct object_array_entry *p, void *data)
|
||||
{
|
||||
add_preferred_base_object(p->name);
|
||||
add_object_entry(p->item->sha1, p->item->type, p->name, 0);
|
||||
p->item->flags |= OBJECT_ADDED;
|
||||
free((char *)p->name);
|
||||
p->name = NULL;
|
||||
}
|
||||
|
||||
static void show_edge(struct commit *commit)
|
||||
@@ -2071,7 +2073,7 @@ static void get_object_list(int ac, const char **av)
|
||||
if (prepare_revision_walk(&revs))
|
||||
die("revision walk setup failed");
|
||||
mark_edges_uninteresting(revs.commits, &revs, show_edge);
|
||||
traverse_commit_list(&revs, show_commit, show_object);
|
||||
traverse_commit_list(&revs, show_commit, show_object, NULL);
|
||||
|
||||
if (keep_unreachable)
|
||||
add_objects_in_unpacked_packs(&revs);
|
||||
|
||||
@@ -15,7 +15,7 @@ static const char * const builtin_remote_usage[] = {
|
||||
"git remote set-head <name> [-a | -d | <branch>]",
|
||||
"git remote show [-n] <name>",
|
||||
"git remote prune [-n | --dry-run] <name>",
|
||||
"git remote [-v | --verbose] update [group]",
|
||||
"git remote [-v | --verbose] update [-p | --prune] [group]",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -26,6 +26,7 @@ static const char * const builtin_remote_usage[] = {
|
||||
static int verbose;
|
||||
|
||||
static int show_all(void);
|
||||
static int prune_remote(const char *remote, int dry_run);
|
||||
|
||||
static inline int postfixcmp(const char *string, const char *postfix)
|
||||
{
|
||||
@@ -1128,46 +1129,49 @@ static int prune(int argc, const char **argv)
|
||||
OPT__DRY_RUN(&dry_run),
|
||||
OPT_END()
|
||||
};
|
||||
struct ref_states states;
|
||||
const char *dangling_msg;
|
||||
|
||||
argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
|
||||
|
||||
if (argc < 1)
|
||||
usage_with_options(builtin_remote_usage, options);
|
||||
|
||||
dangling_msg = (dry_run
|
||||
? " %s will become dangling!\n"
|
||||
: " %s has become dangling!\n");
|
||||
for (; argc; argc--, argv++)
|
||||
result |= prune_remote(*argv, dry_run);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int prune_remote(const char *remote, int dry_run)
|
||||
{
|
||||
int result = 0, i;
|
||||
struct ref_states states;
|
||||
const char *dangling_msg = dry_run
|
||||
? " %s will become dangling!\n"
|
||||
: " %s has become dangling!\n";
|
||||
|
||||
memset(&states, 0, sizeof(states));
|
||||
for (; argc; argc--, argv++) {
|
||||
int i;
|
||||
get_remote_ref_states(remote, &states, GET_REF_STATES);
|
||||
|
||||
get_remote_ref_states(*argv, &states, GET_REF_STATES);
|
||||
|
||||
if (states.stale.nr) {
|
||||
printf("Pruning %s\n", *argv);
|
||||
printf("URL: %s\n",
|
||||
states.remote->url_nr
|
||||
? states.remote->url[0]
|
||||
: "(no URL)");
|
||||
}
|
||||
|
||||
for (i = 0; i < states.stale.nr; i++) {
|
||||
const char *refname = states.stale.items[i].util;
|
||||
|
||||
if (!dry_run)
|
||||
result |= delete_ref(refname, NULL, 0);
|
||||
|
||||
printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
|
||||
abbrev_ref(refname, "refs/remotes/"));
|
||||
warn_dangling_symref(dangling_msg, refname);
|
||||
}
|
||||
|
||||
free_remote_ref_states(&states);
|
||||
if (states.stale.nr) {
|
||||
printf("Pruning %s\n", remote);
|
||||
printf("URL: %s\n",
|
||||
states.remote->url_nr
|
||||
? states.remote->url[0]
|
||||
: "(no URL)");
|
||||
}
|
||||
|
||||
for (i = 0; i < states.stale.nr; i++) {
|
||||
const char *refname = states.stale.items[i].util;
|
||||
|
||||
if (!dry_run)
|
||||
result |= delete_ref(refname, NULL, 0);
|
||||
|
||||
printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
|
||||
abbrev_ref(refname, "refs/remotes/"));
|
||||
warn_dangling_symref(dangling_msg, refname);
|
||||
}
|
||||
|
||||
free_remote_ref_states(&states);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1184,16 +1188,18 @@ struct remote_group {
|
||||
struct string_list *list;
|
||||
} remote_group;
|
||||
|
||||
static int get_remote_group(const char *key, const char *value, void *cb)
|
||||
static int get_remote_group(const char *key, const char *value, void *num_hits)
|
||||
{
|
||||
if (!prefixcmp(key, "remotes.") &&
|
||||
!strcmp(key + 8, remote_group.name)) {
|
||||
/* split list by white space */
|
||||
int space = strcspn(value, " \t\n");
|
||||
while (*value) {
|
||||
if (space > 1)
|
||||
if (space > 1) {
|
||||
string_list_append(xstrndup(value, space),
|
||||
remote_group.list);
|
||||
++*((int *)num_hits);
|
||||
}
|
||||
value += space + (value[space] != '\0');
|
||||
space = strcspn(value, " \t\n");
|
||||
}
|
||||
@@ -1204,10 +1210,18 @@ static int get_remote_group(const char *key, const char *value, void *cb)
|
||||
|
||||
static int update(int argc, const char **argv)
|
||||
{
|
||||
int i, result = 0;
|
||||
int i, result = 0, prune = 0;
|
||||
struct string_list list = { NULL, 0, 0, 0 };
|
||||
static const char *default_argv[] = { NULL, "default", NULL };
|
||||
struct option options[] = {
|
||||
OPT_GROUP("update specific options"),
|
||||
OPT_BOOLEAN('p', "prune", &prune,
|
||||
"prune remotes after fecthing"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, builtin_remote_usage,
|
||||
PARSE_OPT_KEEP_ARGV0);
|
||||
if (argc < 2) {
|
||||
argc = 2;
|
||||
argv = default_argv;
|
||||
@@ -1215,15 +1229,28 @@ static int update(int argc, const char **argv)
|
||||
|
||||
remote_group.list = &list;
|
||||
for (i = 1; i < argc; i++) {
|
||||
int groups_found = 0;
|
||||
remote_group.name = argv[i];
|
||||
result = git_config(get_remote_group, NULL);
|
||||
result = git_config(get_remote_group, &groups_found);
|
||||
if (!groups_found && (i != 1 || strcmp(argv[1], "default"))) {
|
||||
struct remote *remote;
|
||||
if (!remote_is_configured(argv[i]))
|
||||
die("No such remote or remote group: %s",
|
||||
argv[i]);
|
||||
remote = remote_get(argv[i]);
|
||||
string_list_append(remote->name, remote_group.list);
|
||||
}
|
||||
}
|
||||
|
||||
if (!result && !list.nr && argc == 2 && !strcmp(argv[1], "default"))
|
||||
result = for_each_remote(get_one_remote_for_update, &list);
|
||||
|
||||
for (i = 0; i < list.nr; i++)
|
||||
result |= fetch_remote(list.items[i].string);
|
||||
for (i = 0; i < list.nr; i++) {
|
||||
int err = fetch_remote(list.items[i].string);
|
||||
result |= err;
|
||||
if (!err && prune)
|
||||
result |= prune_remote(list.items[i].string, 0);
|
||||
}
|
||||
|
||||
/* all names were strdup()ed or strndup()ed */
|
||||
list.strdup_strings = 1;
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
#include "cache.h"
|
||||
#include "refs.h"
|
||||
#include "tag.h"
|
||||
#include "commit.h"
|
||||
#include "tree.h"
|
||||
#include "blob.h"
|
||||
#include "tree-walk.h"
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
#include "list-objects.h"
|
||||
#include "builtin.h"
|
||||
#include "log-tree.h"
|
||||
#include "graph.h"
|
||||
|
||||
/* bits #0-15 in revision.h */
|
||||
|
||||
#define COUNTED (1u<<16)
|
||||
#include "bisect.h"
|
||||
|
||||
static const char rev_list_usage[] =
|
||||
"git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
|
||||
@@ -50,73 +42,69 @@ static const char rev_list_usage[] =
|
||||
" --bisect-all"
|
||||
;
|
||||
|
||||
static struct rev_info revs;
|
||||
|
||||
static int bisect_list;
|
||||
static int show_timestamp;
|
||||
static int hdr_termination;
|
||||
static const char *header_prefix;
|
||||
|
||||
static void finish_commit(struct commit *commit);
|
||||
static void show_commit(struct commit *commit)
|
||||
static void finish_commit(struct commit *commit, void *data);
|
||||
static void show_commit(struct commit *commit, void *data)
|
||||
{
|
||||
graph_show_commit(revs.graph);
|
||||
struct rev_list_info *info = data;
|
||||
struct rev_info *revs = info->revs;
|
||||
|
||||
if (show_timestamp)
|
||||
graph_show_commit(revs->graph);
|
||||
|
||||
if (info->show_timestamp)
|
||||
printf("%lu ", commit->date);
|
||||
if (header_prefix)
|
||||
fputs(header_prefix, stdout);
|
||||
if (info->header_prefix)
|
||||
fputs(info->header_prefix, stdout);
|
||||
|
||||
if (!revs.graph) {
|
||||
if (!revs->graph) {
|
||||
if (commit->object.flags & BOUNDARY)
|
||||
putchar('-');
|
||||
else if (commit->object.flags & UNINTERESTING)
|
||||
putchar('^');
|
||||
else if (revs.left_right) {
|
||||
else if (revs->left_right) {
|
||||
if (commit->object.flags & SYMMETRIC_LEFT)
|
||||
putchar('<');
|
||||
else
|
||||
putchar('>');
|
||||
}
|
||||
}
|
||||
if (revs.abbrev_commit && revs.abbrev)
|
||||
fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev),
|
||||
if (revs->abbrev_commit && revs->abbrev)
|
||||
fputs(find_unique_abbrev(commit->object.sha1, revs->abbrev),
|
||||
stdout);
|
||||
else
|
||||
fputs(sha1_to_hex(commit->object.sha1), stdout);
|
||||
if (revs.print_parents) {
|
||||
if (revs->print_parents) {
|
||||
struct commit_list *parents = commit->parents;
|
||||
while (parents) {
|
||||
printf(" %s", sha1_to_hex(parents->item->object.sha1));
|
||||
parents = parents->next;
|
||||
}
|
||||
}
|
||||
if (revs.children.name) {
|
||||
if (revs->children.name) {
|
||||
struct commit_list *children;
|
||||
|
||||
children = lookup_decoration(&revs.children, &commit->object);
|
||||
children = lookup_decoration(&revs->children, &commit->object);
|
||||
while (children) {
|
||||
printf(" %s", sha1_to_hex(children->item->object.sha1));
|
||||
children = children->next;
|
||||
}
|
||||
}
|
||||
show_decorations(&revs, commit);
|
||||
if (revs.commit_format == CMIT_FMT_ONELINE)
|
||||
show_decorations(revs, commit);
|
||||
if (revs->commit_format == CMIT_FMT_ONELINE)
|
||||
putchar(' ');
|
||||
else
|
||||
putchar('\n');
|
||||
|
||||
if (revs.verbose_header && commit->buffer) {
|
||||
if (revs->verbose_header && commit->buffer) {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
pretty_print_commit(revs.commit_format, commit,
|
||||
&buf, revs.abbrev, NULL, NULL,
|
||||
revs.date_mode, 0);
|
||||
if (revs.graph) {
|
||||
pretty_print_commit(revs->commit_format, commit,
|
||||
&buf, revs->abbrev, NULL, NULL,
|
||||
revs->date_mode, 0);
|
||||
if (revs->graph) {
|
||||
if (buf.len) {
|
||||
if (revs.commit_format != CMIT_FMT_ONELINE)
|
||||
graph_show_oneline(revs.graph);
|
||||
if (revs->commit_format != CMIT_FMT_ONELINE)
|
||||
graph_show_oneline(revs->graph);
|
||||
|
||||
graph_show_commit_msg(revs.graph, &buf);
|
||||
graph_show_commit_msg(revs->graph, &buf);
|
||||
|
||||
/*
|
||||
* Add a newline after the commit message.
|
||||
@@ -134,7 +122,7 @@ static void show_commit(struct commit *commit)
|
||||
* format doesn't explicitly end in a newline.)
|
||||
*/
|
||||
if (buf.len && buf.buf[buf.len - 1] == '\n')
|
||||
graph_show_padding(revs.graph);
|
||||
graph_show_padding(revs->graph);
|
||||
putchar('\n');
|
||||
} else {
|
||||
/*
|
||||
@@ -142,23 +130,23 @@ static void show_commit(struct commit *commit)
|
||||
* the rest of the graph output for this
|
||||
* commit.
|
||||
*/
|
||||
if (graph_show_remainder(revs.graph))
|
||||
if (graph_show_remainder(revs->graph))
|
||||
putchar('\n');
|
||||
}
|
||||
} else {
|
||||
if (buf.len)
|
||||
printf("%s%c", buf.buf, hdr_termination);
|
||||
printf("%s%c", buf.buf, info->hdr_termination);
|
||||
}
|
||||
strbuf_release(&buf);
|
||||
} else {
|
||||
if (graph_show_remainder(revs.graph))
|
||||
if (graph_show_remainder(revs->graph))
|
||||
putchar('\n');
|
||||
}
|
||||
maybe_flush_or_die(stdout, "stdout");
|
||||
finish_commit(commit);
|
||||
finish_commit(commit, data);
|
||||
}
|
||||
|
||||
static void finish_commit(struct commit *commit)
|
||||
static void finish_commit(struct commit *commit, void *data)
|
||||
{
|
||||
if (commit->parents) {
|
||||
free_commit_list(commit->parents);
|
||||
@@ -168,20 +156,20 @@ static void finish_commit(struct commit *commit)
|
||||
commit->buffer = NULL;
|
||||
}
|
||||
|
||||
static void finish_object(struct object_array_entry *p)
|
||||
static void finish_object(struct object_array_entry *p, void *data)
|
||||
{
|
||||
if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1))
|
||||
die("missing blob object '%s'", sha1_to_hex(p->item->sha1));
|
||||
}
|
||||
|
||||
static void show_object(struct object_array_entry *p)
|
||||
static void show_object(struct object_array_entry *p, void *data)
|
||||
{
|
||||
/* An object with name "foo\n0000000..." can be used to
|
||||
* confuse downstream "git pack-objects" very badly.
|
||||
*/
|
||||
const char *ep = strchr(p->name, '\n');
|
||||
|
||||
finish_object(p);
|
||||
finish_object(p, data);
|
||||
if (ep) {
|
||||
printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
|
||||
(int) (ep - p->name),
|
||||
@@ -196,384 +184,6 @@ static void show_edge(struct commit *commit)
|
||||
printf("-%s\n", sha1_to_hex(commit->object.sha1));
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a truly stupid algorithm, but it's only
|
||||
* used for bisection, and we just don't care enough.
|
||||
*
|
||||
* We care just barely enough to avoid recursing for
|
||||
* non-merge entries.
|
||||
*/
|
||||
static int count_distance(struct commit_list *entry)
|
||||
{
|
||||
int nr = 0;
|
||||
|
||||
while (entry) {
|
||||
struct commit *commit = entry->item;
|
||||
struct commit_list *p;
|
||||
|
||||
if (commit->object.flags & (UNINTERESTING | COUNTED))
|
||||
break;
|
||||
if (!(commit->object.flags & TREESAME))
|
||||
nr++;
|
||||
commit->object.flags |= COUNTED;
|
||||
p = commit->parents;
|
||||
entry = p;
|
||||
if (p) {
|
||||
p = p->next;
|
||||
while (p) {
|
||||
nr += count_distance(p);
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nr;
|
||||
}
|
||||
|
||||
static void clear_distance(struct commit_list *list)
|
||||
{
|
||||
while (list) {
|
||||
struct commit *commit = list->item;
|
||||
commit->object.flags &= ~COUNTED;
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
#define DEBUG_BISECT 0
|
||||
|
||||
static inline int weight(struct commit_list *elem)
|
||||
{
|
||||
return *((int*)(elem->item->util));
|
||||
}
|
||||
|
||||
static inline void weight_set(struct commit_list *elem, int weight)
|
||||
{
|
||||
*((int*)(elem->item->util)) = weight;
|
||||
}
|
||||
|
||||
static int count_interesting_parents(struct commit *commit)
|
||||
{
|
||||
struct commit_list *p;
|
||||
int count;
|
||||
|
||||
for (count = 0, p = commit->parents; p; p = p->next) {
|
||||
if (p->item->object.flags & UNINTERESTING)
|
||||
continue;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static inline int halfway(struct commit_list *p, int nr)
|
||||
{
|
||||
/*
|
||||
* Don't short-cut something we are not going to return!
|
||||
*/
|
||||
if (p->item->object.flags & TREESAME)
|
||||
return 0;
|
||||
if (DEBUG_BISECT)
|
||||
return 0;
|
||||
/*
|
||||
* 2 and 3 are halfway of 5.
|
||||
* 3 is halfway of 6 but 2 and 4 are not.
|
||||
*/
|
||||
switch (2 * weight(p) - nr) {
|
||||
case -1: case 0: case 1:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if !DEBUG_BISECT
|
||||
#define show_list(a,b,c,d) do { ; } while (0)
|
||||
#else
|
||||
static void show_list(const char *debug, int counted, int nr,
|
||||
struct commit_list *list)
|
||||
{
|
||||
struct commit_list *p;
|
||||
|
||||
fprintf(stderr, "%s (%d/%d)\n", debug, counted, nr);
|
||||
|
||||
for (p = list; p; p = p->next) {
|
||||
struct commit_list *pp;
|
||||
struct commit *commit = p->item;
|
||||
unsigned flags = commit->object.flags;
|
||||
enum object_type type;
|
||||
unsigned long size;
|
||||
char *buf = read_sha1_file(commit->object.sha1, &type, &size);
|
||||
char *ep, *sp;
|
||||
|
||||
fprintf(stderr, "%c%c%c ",
|
||||
(flags & TREESAME) ? ' ' : 'T',
|
||||
(flags & UNINTERESTING) ? 'U' : ' ',
|
||||
(flags & COUNTED) ? 'C' : ' ');
|
||||
if (commit->util)
|
||||
fprintf(stderr, "%3d", weight(p));
|
||||
else
|
||||
fprintf(stderr, "---");
|
||||
fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.sha1));
|
||||
for (pp = commit->parents; pp; pp = pp->next)
|
||||
fprintf(stderr, " %.*s", 8,
|
||||
sha1_to_hex(pp->item->object.sha1));
|
||||
|
||||
sp = strstr(buf, "\n\n");
|
||||
if (sp) {
|
||||
sp += 2;
|
||||
for (ep = sp; *ep && *ep != '\n'; ep++)
|
||||
;
|
||||
fprintf(stderr, " %.*s", (int)(ep - sp), sp);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
#endif /* DEBUG_BISECT */
|
||||
|
||||
static struct commit_list *best_bisection(struct commit_list *list, int nr)
|
||||
{
|
||||
struct commit_list *p, *best;
|
||||
int best_distance = -1;
|
||||
|
||||
best = list;
|
||||
for (p = list; p; p = p->next) {
|
||||
int distance;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (flags & TREESAME)
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
distance = nr - distance;
|
||||
if (distance > best_distance) {
|
||||
best = p;
|
||||
best_distance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
struct commit_dist {
|
||||
struct commit *commit;
|
||||
int distance;
|
||||
};
|
||||
|
||||
static int compare_commit_dist(const void *a_, const void *b_)
|
||||
{
|
||||
struct commit_dist *a, *b;
|
||||
|
||||
a = (struct commit_dist *)a_;
|
||||
b = (struct commit_dist *)b_;
|
||||
if (a->distance != b->distance)
|
||||
return b->distance - a->distance; /* desc sort */
|
||||
return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
|
||||
}
|
||||
|
||||
static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
|
||||
{
|
||||
struct commit_list *p;
|
||||
struct commit_dist *array = xcalloc(nr, sizeof(*array));
|
||||
int cnt, i;
|
||||
|
||||
for (p = list, cnt = 0; p; p = p->next) {
|
||||
int distance;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (flags & TREESAME)
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
distance = nr - distance;
|
||||
array[cnt].commit = p->item;
|
||||
array[cnt].distance = distance;
|
||||
cnt++;
|
||||
}
|
||||
qsort(array, cnt, sizeof(*array), compare_commit_dist);
|
||||
for (p = list, i = 0; i < cnt; i++) {
|
||||
struct name_decoration *r = xmalloc(sizeof(*r) + 100);
|
||||
struct object *obj = &(array[i].commit->object);
|
||||
|
||||
sprintf(r->name, "dist=%d", array[i].distance);
|
||||
r->next = add_decoration(&name_decoration, obj, r);
|
||||
p->item = array[i].commit;
|
||||
p = p->next;
|
||||
}
|
||||
if (p)
|
||||
p->next = NULL;
|
||||
free(array);
|
||||
return list;
|
||||
}
|
||||
|
||||
/*
|
||||
* zero or positive weight is the number of interesting commits it can
|
||||
* reach, including itself. Especially, weight = 0 means it does not
|
||||
* reach any tree-changing commits (e.g. just above uninteresting one
|
||||
* but traversal is with pathspec).
|
||||
*
|
||||
* weight = -1 means it has one parent and its distance is yet to
|
||||
* be computed.
|
||||
*
|
||||
* weight = -2 means it has more than one parent and its distance is
|
||||
* unknown. After running count_distance() first, they will get zero
|
||||
* or positive distance.
|
||||
*/
|
||||
static struct commit_list *do_find_bisection(struct commit_list *list,
|
||||
int nr, int *weights,
|
||||
int find_all)
|
||||
{
|
||||
int n, counted;
|
||||
struct commit_list *p;
|
||||
|
||||
counted = 0;
|
||||
|
||||
for (n = 0, p = list; p; p = p->next) {
|
||||
struct commit *commit = p->item;
|
||||
unsigned flags = commit->object.flags;
|
||||
|
||||
p->item->util = &weights[n++];
|
||||
switch (count_interesting_parents(commit)) {
|
||||
case 0:
|
||||
if (!(flags & TREESAME)) {
|
||||
weight_set(p, 1);
|
||||
counted++;
|
||||
show_list("bisection 2 count one",
|
||||
counted, nr, list);
|
||||
}
|
||||
/*
|
||||
* otherwise, it is known not to reach any
|
||||
* tree-changing commit and gets weight 0.
|
||||
*/
|
||||
break;
|
||||
case 1:
|
||||
weight_set(p, -1);
|
||||
break;
|
||||
default:
|
||||
weight_set(p, -2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
show_list("bisection 2 initialize", counted, nr, list);
|
||||
|
||||
/*
|
||||
* If you have only one parent in the resulting set
|
||||
* then you can reach one commit more than that parent
|
||||
* can reach. So we do not have to run the expensive
|
||||
* count_distance() for single strand of pearls.
|
||||
*
|
||||
* However, if you have more than one parents, you cannot
|
||||
* just add their distance and one for yourself, since
|
||||
* they usually reach the same ancestor and you would
|
||||
* end up counting them twice that way.
|
||||
*
|
||||
* So we will first count distance of merges the usual
|
||||
* way, and then fill the blanks using cheaper algorithm.
|
||||
*/
|
||||
for (p = list; p; p = p->next) {
|
||||
if (p->item->object.flags & UNINTERESTING)
|
||||
continue;
|
||||
if (weight(p) != -2)
|
||||
continue;
|
||||
weight_set(p, count_distance(p));
|
||||
clear_distance(list);
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
if (!find_all && halfway(p, nr))
|
||||
return p;
|
||||
counted++;
|
||||
}
|
||||
|
||||
show_list("bisection 2 count_distance", counted, nr, list);
|
||||
|
||||
while (counted < nr) {
|
||||
for (p = list; p; p = p->next) {
|
||||
struct commit_list *q;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (0 <= weight(p))
|
||||
continue;
|
||||
for (q = p->item->parents; q; q = q->next) {
|
||||
if (q->item->object.flags & UNINTERESTING)
|
||||
continue;
|
||||
if (0 <= weight(q))
|
||||
break;
|
||||
}
|
||||
if (!q)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* weight for p is unknown but q is known.
|
||||
* add one for p itself if p is to be counted,
|
||||
* otherwise inherit it from q directly.
|
||||
*/
|
||||
if (!(flags & TREESAME)) {
|
||||
weight_set(p, weight(q)+1);
|
||||
counted++;
|
||||
show_list("bisection 2 count one",
|
||||
counted, nr, list);
|
||||
}
|
||||
else
|
||||
weight_set(p, weight(q));
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
if (!find_all && halfway(p, nr))
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
show_list("bisection 2 counted all", counted, nr, list);
|
||||
|
||||
if (!find_all)
|
||||
return best_bisection(list, nr);
|
||||
else
|
||||
return best_bisection_sorted(list, nr);
|
||||
}
|
||||
|
||||
static struct commit_list *find_bisection(struct commit_list *list,
|
||||
int *reaches, int *all,
|
||||
int find_all)
|
||||
{
|
||||
int nr, on_list;
|
||||
struct commit_list *p, *best, *next, *last;
|
||||
int *weights;
|
||||
|
||||
show_list("bisection 2 entry", 0, 0, list);
|
||||
|
||||
/*
|
||||
* Count the number of total and tree-changing items on the
|
||||
* list, while reversing the list.
|
||||
*/
|
||||
for (nr = on_list = 0, last = NULL, p = list;
|
||||
p;
|
||||
p = next) {
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
next = p->next;
|
||||
if (flags & UNINTERESTING)
|
||||
continue;
|
||||
p->next = last;
|
||||
last = p;
|
||||
if (!(flags & TREESAME))
|
||||
nr++;
|
||||
on_list++;
|
||||
}
|
||||
list = last;
|
||||
show_list("bisection 2 sorted", 0, nr, list);
|
||||
|
||||
*all = nr;
|
||||
weights = xcalloc(on_list, sizeof(*weights));
|
||||
|
||||
/* Do the real work of finding bisection commit. */
|
||||
best = do_find_bisection(list, nr, weights, find_all);
|
||||
if (best) {
|
||||
if (!find_all)
|
||||
best->next = NULL;
|
||||
*reaches = weight(best);
|
||||
}
|
||||
free(weights);
|
||||
return best;
|
||||
}
|
||||
|
||||
static inline int log2i(int n)
|
||||
{
|
||||
int log2 = 0;
|
||||
@@ -613,11 +223,83 @@ static int estimate_bisect_steps(int all)
|
||||
return (e < 3 * x) ? n : n - 1;
|
||||
}
|
||||
|
||||
static void show_tried_revs(struct commit_list *tried, int stringed)
|
||||
{
|
||||
printf("bisect_tried='");
|
||||
for (;tried; tried = tried->next) {
|
||||
char *format = tried->next ? "%s|" : "%s";
|
||||
printf(format, sha1_to_hex(tried->item->object.sha1));
|
||||
}
|
||||
printf(stringed ? "' &&\n" : "'\n");
|
||||
}
|
||||
|
||||
int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
|
||||
{
|
||||
int cnt, flags = info->bisect_show_flags;
|
||||
char hex[41] = "", *format;
|
||||
struct commit_list *tried;
|
||||
struct rev_info *revs = info->revs;
|
||||
|
||||
if (!revs->commits && !(flags & BISECT_SHOW_TRIED))
|
||||
return 1;
|
||||
|
||||
revs->commits = filter_skipped(revs->commits, &tried, flags & BISECT_SHOW_ALL);
|
||||
|
||||
/*
|
||||
* revs->commits can reach "reaches" commits among
|
||||
* "all" commits. If it is good, then there are
|
||||
* (all-reaches) commits left to be bisected.
|
||||
* On the other hand, if it is bad, then the set
|
||||
* to bisect is "reaches".
|
||||
* A bisect set of size N has (N-1) commits further
|
||||
* to test, as we already know one bad one.
|
||||
*/
|
||||
cnt = all - reaches;
|
||||
if (cnt < reaches)
|
||||
cnt = reaches;
|
||||
|
||||
if (revs->commits)
|
||||
strcpy(hex, sha1_to_hex(revs->commits->item->object.sha1));
|
||||
|
||||
if (flags & BISECT_SHOW_ALL) {
|
||||
traverse_commit_list(revs, show_commit, show_object, info);
|
||||
printf("------\n");
|
||||
}
|
||||
|
||||
if (flags & BISECT_SHOW_TRIED)
|
||||
show_tried_revs(tried, flags & BISECT_SHOW_STRINGED);
|
||||
format = (flags & BISECT_SHOW_STRINGED) ?
|
||||
"bisect_rev=%s &&\n"
|
||||
"bisect_nr=%d &&\n"
|
||||
"bisect_good=%d &&\n"
|
||||
"bisect_bad=%d &&\n"
|
||||
"bisect_all=%d &&\n"
|
||||
"bisect_steps=%d\n"
|
||||
:
|
||||
"bisect_rev=%s\n"
|
||||
"bisect_nr=%d\n"
|
||||
"bisect_good=%d\n"
|
||||
"bisect_bad=%d\n"
|
||||
"bisect_all=%d\n"
|
||||
"bisect_steps=%d\n";
|
||||
printf(format,
|
||||
hex,
|
||||
cnt - 1,
|
||||
all - reaches - 1,
|
||||
reaches - 1,
|
||||
all,
|
||||
estimate_bisect_steps(all));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct commit_list *list;
|
||||
struct rev_info revs;
|
||||
struct rev_list_info info;
|
||||
int i;
|
||||
int read_from_stdin = 0;
|
||||
int bisect_list = 0;
|
||||
int bisect_show_vars = 0;
|
||||
int bisect_find_all = 0;
|
||||
int quiet = 0;
|
||||
@@ -628,6 +310,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
revs.commit_format = CMIT_FMT_UNSPECIFIED;
|
||||
argc = setup_revisions(argc, argv, &revs, NULL);
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.revs = &revs;
|
||||
|
||||
quiet = DIFF_OPT_TST(&revs.diffopt, QUIET);
|
||||
for (i = 1 ; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
@@ -637,7 +322,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--timestamp")) {
|
||||
show_timestamp = 1;
|
||||
info.show_timestamp = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--bisect")) {
|
||||
@@ -647,6 +332,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
if (!strcmp(arg, "--bisect-all")) {
|
||||
bisect_list = 1;
|
||||
bisect_find_all = 1;
|
||||
info.bisect_show_flags = BISECT_SHOW_ALL;
|
||||
revs.show_decorations = 1;
|
||||
continue;
|
||||
}
|
||||
@@ -666,19 +352,17 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
|
||||
/* The command line has a --pretty */
|
||||
hdr_termination = '\n';
|
||||
info.hdr_termination = '\n';
|
||||
if (revs.commit_format == CMIT_FMT_ONELINE)
|
||||
header_prefix = "";
|
||||
info.header_prefix = "";
|
||||
else
|
||||
header_prefix = "commit ";
|
||||
info.header_prefix = "commit ";
|
||||
}
|
||||
else if (revs.verbose_header)
|
||||
/* Only --header was specified */
|
||||
revs.commit_format = CMIT_FMT_RAW;
|
||||
|
||||
list = revs.commits;
|
||||
|
||||
if ((!list &&
|
||||
if ((!revs.commits &&
|
||||
(!(revs.tag_objects||revs.tree_objects||revs.blob_objects) &&
|
||||
!revs.pending.nr)) ||
|
||||
revs.diff)
|
||||
@@ -699,49 +383,15 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
|
||||
revs.commits = find_bisection(revs.commits, &reaches, &all,
|
||||
bisect_find_all);
|
||||
if (bisect_show_vars) {
|
||||
int cnt;
|
||||
char hex[41];
|
||||
if (!revs.commits)
|
||||
return 1;
|
||||
/*
|
||||
* revs.commits can reach "reaches" commits among
|
||||
* "all" commits. If it is good, then there are
|
||||
* (all-reaches) commits left to be bisected.
|
||||
* On the other hand, if it is bad, then the set
|
||||
* to bisect is "reaches".
|
||||
* A bisect set of size N has (N-1) commits further
|
||||
* to test, as we already know one bad one.
|
||||
*/
|
||||
cnt = all - reaches;
|
||||
if (cnt < reaches)
|
||||
cnt = reaches;
|
||||
strcpy(hex, sha1_to_hex(revs.commits->item->object.sha1));
|
||||
|
||||
if (bisect_find_all) {
|
||||
traverse_commit_list(&revs, show_commit, show_object);
|
||||
printf("------\n");
|
||||
}
|
||||
|
||||
printf("bisect_rev=%s\n"
|
||||
"bisect_nr=%d\n"
|
||||
"bisect_good=%d\n"
|
||||
"bisect_bad=%d\n"
|
||||
"bisect_all=%d\n"
|
||||
"bisect_steps=%d\n",
|
||||
hex,
|
||||
cnt - 1,
|
||||
all - reaches - 1,
|
||||
reaches - 1,
|
||||
all,
|
||||
estimate_bisect_steps(all));
|
||||
return 0;
|
||||
}
|
||||
if (bisect_show_vars)
|
||||
return show_bisect_vars(&info, reaches, all);
|
||||
}
|
||||
|
||||
traverse_commit_list(&revs,
|
||||
quiet ? finish_commit : show_commit,
|
||||
quiet ? finish_object : show_object);
|
||||
quiet ? finish_commit : show_commit,
|
||||
quiet ? finish_object : show_object,
|
||||
&info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ extern int cmd_add(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_annotate(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_apply(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_archive(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_bisect__helper(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_blame(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_branch(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_bundle(int argc, const char **argv, const char *prefix);
|
||||
|
||||
17
diff.c
17
diff.c
@@ -62,6 +62,15 @@ static int parse_diff_color_slot(const char *var, int ofs)
|
||||
die("bad config variable '%s'", var);
|
||||
}
|
||||
|
||||
static int git_config_rename(const char *var, const char *value)
|
||||
{
|
||||
if (!value)
|
||||
return DIFF_DETECT_RENAME;
|
||||
if (!strcasecmp(value, "copies") || !strcasecmp(value, "copy"))
|
||||
return DIFF_DETECT_COPY;
|
||||
return git_config_bool(var,value) ? DIFF_DETECT_RENAME : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* These are to give UI layer defaults.
|
||||
* The core-level commands such as git-diff-files should
|
||||
@@ -75,13 +84,7 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "diff.renames")) {
|
||||
if (!value)
|
||||
diff_detect_rename_default = DIFF_DETECT_RENAME;
|
||||
else if (!strcasecmp(value, "copies") ||
|
||||
!strcasecmp(value, "copy"))
|
||||
diff_detect_rename_default = DIFF_DETECT_COPY;
|
||||
else if (git_config_bool(var,value))
|
||||
diff_detect_rename_default = DIFF_DETECT_RENAME;
|
||||
diff_detect_rename_default = git_config_rename(var, value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "diff.autorefreshindex")) {
|
||||
|
||||
@@ -279,87 +279,14 @@ bisect_auto_next() {
|
||||
bisect_next_check && bisect_next || :
|
||||
}
|
||||
|
||||
filter_skipped() {
|
||||
_eval="$1"
|
||||
_skip="$2"
|
||||
|
||||
if [ -z "$_skip" ]; then
|
||||
eval "$_eval" | {
|
||||
while read line
|
||||
do
|
||||
echo "$line &&"
|
||||
done
|
||||
echo ':'
|
||||
}
|
||||
return
|
||||
fi
|
||||
|
||||
# Let's parse the output of:
|
||||
# "git rev-list --bisect-vars --bisect-all ..."
|
||||
eval "$_eval" | {
|
||||
VARS= FOUND= TRIED=
|
||||
while read hash line
|
||||
do
|
||||
case "$VARS,$FOUND,$TRIED,$hash" in
|
||||
1,*,*,*)
|
||||
# "bisect_foo=bar" read from rev-list output.
|
||||
echo "$hash &&"
|
||||
;;
|
||||
,*,*,---*)
|
||||
# Separator
|
||||
;;
|
||||
,,,bisect_rev*)
|
||||
# We had nothing to search.
|
||||
echo "bisect_rev= &&"
|
||||
VARS=1
|
||||
;;
|
||||
,,*,bisect_rev*)
|
||||
# We did not find a good bisect rev.
|
||||
# This should happen only if the "bad"
|
||||
# commit is also a "skip" commit.
|
||||
echo "bisect_rev='$TRIED' &&"
|
||||
VARS=1
|
||||
;;
|
||||
,,*,*)
|
||||
# We are searching.
|
||||
TRIED="${TRIED:+$TRIED|}$hash"
|
||||
case "$_skip" in
|
||||
*$hash*) ;;
|
||||
*)
|
||||
echo "bisect_rev=$hash &&"
|
||||
echo "bisect_tried='$TRIED' &&"
|
||||
FOUND=1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
,1,*,bisect_rev*)
|
||||
# We have already found a rev to be tested.
|
||||
VARS=1
|
||||
;;
|
||||
,1,*,*)
|
||||
;;
|
||||
*)
|
||||
# Unexpected input
|
||||
echo "die 'filter_skipped error'"
|
||||
die "filter_skipped error " \
|
||||
"VARS: '$VARS' " \
|
||||
"FOUND: '$FOUND' " \
|
||||
"TRIED: '$TRIED' " \
|
||||
"hash: '$hash' " \
|
||||
"line: '$line'"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
echo ':'
|
||||
}
|
||||
}
|
||||
|
||||
exit_if_skipped_commits () {
|
||||
_tried=$1
|
||||
if expr "$_tried" : ".*[|].*" > /dev/null ; then
|
||||
_bad=$2
|
||||
if test -n "$_tried" ; then
|
||||
echo "There are only 'skip'ped commit left to test."
|
||||
echo "The first bad commit could be any of:"
|
||||
echo "$_tried" | tr '[|]' '[\012]'
|
||||
test -n "$_bad" && echo "$_bad"
|
||||
echo "We cannot bisect more!"
|
||||
exit 2
|
||||
fi
|
||||
@@ -490,28 +417,23 @@ bisect_next() {
|
||||
test "$?" -eq "1" && return
|
||||
|
||||
# Get bisection information
|
||||
BISECT_OPT=''
|
||||
test -n "$skip" && BISECT_OPT='--bisect-all'
|
||||
eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
|
||||
eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
|
||||
eval=$(filter_skipped "$eval" "$skip") &&
|
||||
eval=$(eval "git bisect--helper --next-vars") &&
|
||||
eval "$eval" || exit
|
||||
|
||||
if [ -z "$bisect_rev" ]; then
|
||||
# We should exit here only if the "bad"
|
||||
# commit is also a "skip" commit (see above).
|
||||
exit_if_skipped_commits "$bisect_tried"
|
||||
echo "$bad was both good and bad"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$bisect_rev" = "$bad" ]; then
|
||||
exit_if_skipped_commits "$bisect_tried"
|
||||
exit_if_skipped_commits "$bisect_tried" "$bad"
|
||||
echo "$bisect_rev is first bad commit"
|
||||
git diff-tree --pretty $bisect_rev
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# We should exit here only if the "bad"
|
||||
# commit is also a "skip" commit (see above).
|
||||
exit_if_skipped_commits "$bisect_rev"
|
||||
|
||||
bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this (roughly $bisect_steps steps)"
|
||||
}
|
||||
|
||||
|
||||
102
git-svn.perl
102
git-svn.perl
@@ -47,7 +47,8 @@ BEGIN {
|
||||
# import functions from Git into our packages, en masse
|
||||
no strict 'refs';
|
||||
foreach (qw/command command_oneline command_noisy command_output_pipe
|
||||
command_input_pipe command_close_pipe/) {
|
||||
command_input_pipe command_close_pipe
|
||||
command_bidi_pipe command_close_bidi_pipe/) {
|
||||
for my $package ( qw(SVN::Git::Editor SVN::Git::Fetcher
|
||||
Git::SVN::Migration Git::SVN::Log Git::SVN),
|
||||
__PACKAGE__) {
|
||||
@@ -63,7 +64,7 @@ $sha1_short = qr/[a-f\d]{4,40}/;
|
||||
my ($_stdin, $_help, $_edit,
|
||||
$_message, $_file,
|
||||
$_template, $_shared,
|
||||
$_version, $_fetch_all, $_no_rebase,
|
||||
$_version, $_fetch_all, $_no_rebase, $_fetch_parent,
|
||||
$_merge, $_strategy, $_dry_run, $_local,
|
||||
$_prefix, $_no_checkout, $_url, $_verbose,
|
||||
$_git_format, $_commit_url, $_tag);
|
||||
@@ -112,6 +113,7 @@ my %cmd = (
|
||||
fetch => [ \&cmd_fetch, "Download new revisions from SVN",
|
||||
{ 'revision|r=s' => \$_revision,
|
||||
'fetch-all|all' => \$_fetch_all,
|
||||
'parent|p' => \$_fetch_parent,
|
||||
%fc_opts } ],
|
||||
clone => [ \&cmd_clone, "Initialize and fetch revisions",
|
||||
{ 'revision|r=s' => \$_revision,
|
||||
@@ -326,6 +328,7 @@ sub do_git_init_db {
|
||||
command_noisy(@init_db);
|
||||
$_repository = Git->repository(Repository => ".git");
|
||||
}
|
||||
command_noisy('config', 'core.autocrlf', 'false');
|
||||
my $set;
|
||||
my $pfx = "svn-remote.$Git::SVN::default_repo_id";
|
||||
foreach my $i (keys %icv) {
|
||||
@@ -334,6 +337,9 @@ sub do_git_init_db {
|
||||
command_noisy('config', "$pfx.$i", $icv{$i});
|
||||
$set = $i;
|
||||
}
|
||||
my $ignore_regex = \$SVN::Git::Fetcher::_ignore_regex;
|
||||
command_noisy('config', "$pfx.ignore-paths", $$ignore_regex)
|
||||
if defined $$ignore_regex;
|
||||
}
|
||||
|
||||
sub init_subdir {
|
||||
@@ -381,12 +387,21 @@ sub cmd_fetch {
|
||||
}
|
||||
my ($remote) = @_;
|
||||
if (@_ > 1) {
|
||||
die "Usage: $0 fetch [--all] [svn-remote]\n";
|
||||
die "Usage: $0 fetch [--all] [--parent] [svn-remote]\n";
|
||||
}
|
||||
$remote ||= $Git::SVN::default_repo_id;
|
||||
if ($_fetch_all) {
|
||||
if ($_fetch_parent) {
|
||||
my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
|
||||
unless ($gs) {
|
||||
die "Unable to determine upstream SVN information from ",
|
||||
"working tree history\n";
|
||||
}
|
||||
# just fetch, don't checkout.
|
||||
$_no_checkout = 'true';
|
||||
$_fetch_all ? $gs->fetch_all : $gs->fetch;
|
||||
} elsif ($_fetch_all) {
|
||||
cmd_multi_fetch();
|
||||
} else {
|
||||
$remote ||= $Git::SVN::default_repo_id;
|
||||
Git::SVN::fetch_all($remote, Git::SVN::read_all_remotes());
|
||||
}
|
||||
}
|
||||
@@ -1254,6 +1269,40 @@ sub cmt_metadata {
|
||||
command(qw/cat-file commit/, shift)))[-1]);
|
||||
}
|
||||
|
||||
sub cmt_sha2rev_batch {
|
||||
my %s2r;
|
||||
my ($pid, $in, $out, $ctx) = command_bidi_pipe(qw/cat-file --batch/);
|
||||
my $list = shift;
|
||||
|
||||
foreach my $sha (@{$list}) {
|
||||
my $first = 1;
|
||||
my $size = 0;
|
||||
print $out $sha, "\n";
|
||||
|
||||
while (my $line = <$in>) {
|
||||
if ($first && $line =~ /^[[:xdigit:]]{40}\smissing$/) {
|
||||
last;
|
||||
} elsif ($first &&
|
||||
$line =~ /^[[:xdigit:]]{40}\scommit\s(\d+)$/) {
|
||||
$first = 0;
|
||||
$size = $1;
|
||||
next;
|
||||
} elsif ($line =~ /^(git-svn-id: )/) {
|
||||
my (undef, $rev, undef) =
|
||||
extract_metadata($line);
|
||||
$s2r{$sha} = $rev;
|
||||
}
|
||||
|
||||
$size -= length($line);
|
||||
last if ($size == 0);
|
||||
}
|
||||
}
|
||||
|
||||
command_close_bidi_pipe($pid, $in, $out, $ctx);
|
||||
|
||||
return \%s2r;
|
||||
}
|
||||
|
||||
sub working_head_info {
|
||||
my ($head, $refs) = @_;
|
||||
my @args = ('log', '--no-color', '--first-parent', '--pretty=medium');
|
||||
@@ -3286,6 +3335,8 @@ sub new {
|
||||
$self->{empty_symlinks} =
|
||||
_mark_empty_symlinks($git_svn, $switch_path);
|
||||
}
|
||||
$self->{ignore_regex} = eval { command_oneline('config', '--get',
|
||||
"svn-remote.$git_svn->{repo_id}.ignore-paths") };
|
||||
$self->{empty} = {};
|
||||
$self->{dir_prop} = {};
|
||||
$self->{file_prop} = {};
|
||||
@@ -3350,8 +3401,10 @@ sub in_dot_git {
|
||||
|
||||
# return value: 0 -- don't ignore, 1 -- ignore
|
||||
sub is_path_ignored {
|
||||
my ($path) = @_;
|
||||
my ($self, $path) = @_;
|
||||
return 1 if in_dot_git($path);
|
||||
return 1 if defined($self->{ignore_regex}) &&
|
||||
$path =~ m!$self->{ignore_regex}!;
|
||||
return 0 unless defined($_ignore_regex);
|
||||
return 1 if $path =~ m!$_ignore_regex!o;
|
||||
return 0;
|
||||
@@ -3382,7 +3435,7 @@ sub git_path {
|
||||
|
||||
sub delete_entry {
|
||||
my ($self, $path, $rev, $pb) = @_;
|
||||
return undef if is_path_ignored($path);
|
||||
return undef if $self->is_path_ignored($path);
|
||||
|
||||
my $gpath = $self->git_path($path);
|
||||
return undef if ($gpath eq '');
|
||||
@@ -3415,7 +3468,7 @@ sub open_file {
|
||||
my ($self, $path, $pb, $rev) = @_;
|
||||
my ($mode, $blob);
|
||||
|
||||
goto out if is_path_ignored($path);
|
||||
goto out if $self->is_path_ignored($path);
|
||||
|
||||
my $gpath = $self->git_path($path);
|
||||
($mode, $blob) = (command('ls-tree', '-z', $self->{c}, "./$gpath")
|
||||
@@ -3435,7 +3488,7 @@ sub add_file {
|
||||
my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
|
||||
my $mode;
|
||||
|
||||
if (!is_path_ignored($path)) {
|
||||
if (!$self->is_path_ignored($path)) {
|
||||
my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
|
||||
delete $self->{empty}->{$dir};
|
||||
$mode = '100644';
|
||||
@@ -3446,7 +3499,7 @@ sub add_file {
|
||||
|
||||
sub add_directory {
|
||||
my ($self, $path, $cp_path, $cp_rev) = @_;
|
||||
goto out if is_path_ignored($path);
|
||||
goto out if $self->is_path_ignored($path);
|
||||
my $gpath = $self->git_path($path);
|
||||
if ($gpath eq '') {
|
||||
my ($ls, $ctx) = command_output_pipe(qw/ls-tree
|
||||
@@ -3470,7 +3523,7 @@ out:
|
||||
|
||||
sub change_dir_prop {
|
||||
my ($self, $db, $prop, $value) = @_;
|
||||
return undef if is_path_ignored($db->{path});
|
||||
return undef if $self->is_path_ignored($db->{path});
|
||||
$self->{dir_prop}->{$db->{path}} ||= {};
|
||||
$self->{dir_prop}->{$db->{path}}->{$prop} = $value;
|
||||
undef;
|
||||
@@ -3478,7 +3531,7 @@ sub change_dir_prop {
|
||||
|
||||
sub absent_directory {
|
||||
my ($self, $path, $pb) = @_;
|
||||
return undef if is_path_ignored($path);
|
||||
return undef if $self->is_path_ignored($path);
|
||||
$self->{absent_dir}->{$pb->{path}} ||= [];
|
||||
push @{$self->{absent_dir}->{$pb->{path}}}, $path;
|
||||
undef;
|
||||
@@ -3486,7 +3539,7 @@ sub absent_directory {
|
||||
|
||||
sub absent_file {
|
||||
my ($self, $path, $pb) = @_;
|
||||
return undef if is_path_ignored($path);
|
||||
return undef if $self->is_path_ignored($path);
|
||||
$self->{absent_file}->{$pb->{path}} ||= [];
|
||||
push @{$self->{absent_file}->{$pb->{path}}}, $path;
|
||||
undef;
|
||||
@@ -3494,7 +3547,7 @@ sub absent_file {
|
||||
|
||||
sub change_file_prop {
|
||||
my ($self, $fb, $prop, $value) = @_;
|
||||
return undef if is_path_ignored($fb->{path});
|
||||
return undef if $self->is_path_ignored($fb->{path});
|
||||
if ($prop eq 'svn:executable') {
|
||||
if ($fb->{mode_b} != 120000) {
|
||||
$fb->{mode_b} = defined $value ? 100755 : 100644;
|
||||
@@ -3510,7 +3563,7 @@ sub change_file_prop {
|
||||
|
||||
sub apply_textdelta {
|
||||
my ($self, $fb, $exp) = @_;
|
||||
return undef if is_path_ignored($fb->{path});
|
||||
return undef if $self->is_path_ignored($fb->{path});
|
||||
my $fh = $::_repository->temp_acquire('svn_delta');
|
||||
# $fh gets auto-closed() by SVN::TxDelta::apply(),
|
||||
# (but $base does not,) so dup() it for reading in close_file
|
||||
@@ -3557,7 +3610,7 @@ sub apply_textdelta {
|
||||
|
||||
sub close_file {
|
||||
my ($self, $fb, $exp) = @_;
|
||||
return undef if is_path_ignored($fb->{path});
|
||||
return undef if $self->is_path_ignored($fb->{path});
|
||||
|
||||
my $hash;
|
||||
my $path = $self->git_path($fb->{path});
|
||||
@@ -4991,11 +5044,22 @@ sub cmd_blame {
|
||||
'--', $path);
|
||||
my ($sha1);
|
||||
my %authors;
|
||||
my @buffer;
|
||||
my %dsha; #distinct sha keys
|
||||
|
||||
while (my $line = <$fh>) {
|
||||
push @buffer, $line;
|
||||
if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
|
||||
$sha1 = $1;
|
||||
(undef, $rev, undef) = ::cmt_metadata($1);
|
||||
$rev = '0' if (!$rev);
|
||||
$dsha{$1} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
my $s2r = ::cmt_sha2rev_batch([keys %dsha]);
|
||||
|
||||
foreach my $line (@buffer) {
|
||||
if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
|
||||
$rev = $s2r->{$1};
|
||||
$rev = '0' if (!$rev)
|
||||
}
|
||||
elsif ($line =~ /^author (.*)/) {
|
||||
$authors{$rev} = $1;
|
||||
|
||||
1
git.c
1
git.c
@@ -274,6 +274,7 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
{ "annotate", cmd_annotate, RUN_SETUP },
|
||||
{ "apply", cmd_apply },
|
||||
{ "archive", cmd_archive },
|
||||
{ "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "blame", cmd_blame, RUN_SETUP },
|
||||
{ "branch", cmd_branch, RUN_SETUP },
|
||||
{ "bundle", cmd_bundle },
|
||||
|
||||
36
http-push.c
36
http-push.c
@@ -186,6 +186,32 @@ enum dav_header_flag {
|
||||
DAV_HEADER_TIMEOUT = (1u << 2)
|
||||
};
|
||||
|
||||
static char *xml_entities(char *s)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
while (*s) {
|
||||
size_t len = strcspn(s, "\"<>&");
|
||||
strbuf_add(&buf, s, len);
|
||||
s += len;
|
||||
switch (*s) {
|
||||
case '"':
|
||||
strbuf_addstr(&buf, """);
|
||||
break;
|
||||
case '<':
|
||||
strbuf_addstr(&buf, "<");
|
||||
break;
|
||||
case '>':
|
||||
strbuf_addstr(&buf, ">");
|
||||
break;
|
||||
case '&':
|
||||
strbuf_addstr(&buf, "&");
|
||||
break;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
return strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
static struct curl_slist *get_dav_token_headers(struct remote_lock *lock, enum dav_header_flag options)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
@@ -1225,6 +1251,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
|
||||
struct remote_lock *lock = NULL;
|
||||
struct curl_slist *dav_headers = NULL;
|
||||
struct xml_ctx ctx;
|
||||
char *escaped;
|
||||
|
||||
url = xmalloc(strlen(repo->url) + strlen(path) + 1);
|
||||
sprintf(url, "%s%s", repo->url, path);
|
||||
@@ -1259,7 +1286,9 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
|
||||
ep = strchr(ep + 1, '/');
|
||||
}
|
||||
|
||||
strbuf_addf(&out_buffer.buf, LOCK_REQUEST, git_default_email);
|
||||
escaped = xml_entities(git_default_email);
|
||||
strbuf_addf(&out_buffer.buf, LOCK_REQUEST, escaped);
|
||||
free(escaped);
|
||||
|
||||
sprintf(timeout_header, "Timeout: Second-%ld", timeout);
|
||||
dav_headers = curl_slist_append(dav_headers, timeout_header);
|
||||
@@ -1584,8 +1613,11 @@ static int locking_available(void)
|
||||
struct curl_slist *dav_headers = NULL;
|
||||
struct xml_ctx ctx;
|
||||
int lock_flags = 0;
|
||||
char *escaped;
|
||||
|
||||
strbuf_addf(&out_buffer.buf, PROPFIND_SUPPORTEDLOCK_REQUEST, repo->url);
|
||||
escaped = xml_entities(repo->url);
|
||||
strbuf_addf(&out_buffer.buf, PROPFIND_SUPPORTEDLOCK_REQUEST, escaped);
|
||||
free(escaped);
|
||||
|
||||
dav_headers = curl_slist_append(dav_headers, "Depth: 0");
|
||||
dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
|
||||
|
||||
@@ -135,8 +135,9 @@ void mark_edges_uninteresting(struct commit_list *list,
|
||||
}
|
||||
|
||||
void traverse_commit_list(struct rev_info *revs,
|
||||
void (*show_commit)(struct commit *),
|
||||
void (*show_object)(struct object_array_entry *))
|
||||
show_commit_fn show_commit,
|
||||
show_object_fn show_object,
|
||||
void *data)
|
||||
{
|
||||
int i;
|
||||
struct commit *commit;
|
||||
@@ -144,7 +145,7 @@ void traverse_commit_list(struct rev_info *revs,
|
||||
|
||||
while ((commit = get_revision(revs)) != NULL) {
|
||||
process_tree(revs, commit->tree, &objects, NULL, "");
|
||||
show_commit(commit);
|
||||
show_commit(commit, data);
|
||||
}
|
||||
for (i = 0; i < revs->pending.nr; i++) {
|
||||
struct object_array_entry *pending = revs->pending.objects + i;
|
||||
@@ -171,7 +172,7 @@ void traverse_commit_list(struct rev_info *revs,
|
||||
sha1_to_hex(obj->sha1), name);
|
||||
}
|
||||
for (i = 0; i < objects.nr; i++)
|
||||
show_object(&objects.objects[i]);
|
||||
show_object(&objects.objects[i], data);
|
||||
free(objects.objects);
|
||||
if (revs->pending.nr) {
|
||||
free(revs->pending.objects);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#ifndef LIST_OBJECTS_H
|
||||
#define LIST_OBJECTS_H
|
||||
|
||||
typedef void (*show_commit_fn)(struct commit *);
|
||||
typedef void (*show_object_fn)(struct object_array_entry *);
|
||||
typedef void (*show_commit_fn)(struct commit *, void *);
|
||||
typedef void (*show_object_fn)(struct object_array_entry *, void *);
|
||||
typedef void (*show_edge_fn)(struct commit *);
|
||||
|
||||
void traverse_commit_list(struct rev_info *revs, show_commit_fn, show_object_fn);
|
||||
void traverse_commit_list(struct rev_info *, show_commit_fn, show_object_fn, void *);
|
||||
|
||||
void mark_edges_uninteresting(struct commit_list *, struct rev_info *, show_edge_fn);
|
||||
|
||||
|
||||
93
patch-ids.c
93
patch-ids.c
@@ -1,6 +1,7 @@
|
||||
#include "cache.h"
|
||||
#include "diff.h"
|
||||
#include "commit.h"
|
||||
#include "sha1-lookup.h"
|
||||
#include "patch-ids.h"
|
||||
|
||||
static int commit_patch_id(struct commit *commit, struct diff_options *options,
|
||||
@@ -15,99 +16,15 @@ static int commit_patch_id(struct commit *commit, struct diff_options *options,
|
||||
return diff_flush_patch_id(options, sha1);
|
||||
}
|
||||
|
||||
static uint32_t take2(const unsigned char *id)
|
||||
static const unsigned char *patch_id_access(size_t index, void *table)
|
||||
{
|
||||
return ((id[0] << 8) | id[1]);
|
||||
struct patch_id **id_table = table;
|
||||
return id_table[index]->patch_id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Conventional binary search loop looks like this:
|
||||
*
|
||||
* do {
|
||||
* int mi = (lo + hi) / 2;
|
||||
* int cmp = "entry pointed at by mi" minus "target";
|
||||
* if (!cmp)
|
||||
* return (mi is the wanted one)
|
||||
* if (cmp > 0)
|
||||
* hi = mi; "mi is larger than target"
|
||||
* else
|
||||
* lo = mi+1; "mi is smaller than target"
|
||||
* } while (lo < hi);
|
||||
*
|
||||
* The invariants are:
|
||||
*
|
||||
* - When entering the loop, lo points at a slot that is never
|
||||
* above the target (it could be at the target), hi points at a
|
||||
* slot that is guaranteed to be above the target (it can never
|
||||
* be at the target).
|
||||
*
|
||||
* - We find a point 'mi' between lo and hi (mi could be the same
|
||||
* as lo, but never can be the same as hi), and check if it hits
|
||||
* the target. There are three cases:
|
||||
*
|
||||
* - if it is a hit, we are happy.
|
||||
*
|
||||
* - if it is strictly higher than the target, we update hi with
|
||||
* it.
|
||||
*
|
||||
* - if it is strictly lower than the target, we update lo to be
|
||||
* one slot after it, because we allow lo to be at the target.
|
||||
*
|
||||
* When choosing 'mi', we do not have to take the "middle" but
|
||||
* anywhere in between lo and hi, as long as lo <= mi < hi is
|
||||
* satisfied. When we somehow know that the distance between the
|
||||
* target and lo is much shorter than the target and hi, we could
|
||||
* pick mi that is much closer to lo than the midway.
|
||||
*/
|
||||
static int patch_pos(struct patch_id **table, int nr, const unsigned char *id)
|
||||
{
|
||||
int hi = nr;
|
||||
int lo = 0;
|
||||
int mi = 0;
|
||||
|
||||
if (!nr)
|
||||
return -1;
|
||||
|
||||
if (nr != 1) {
|
||||
unsigned lov, hiv, miv, ofs;
|
||||
|
||||
for (ofs = 0; ofs < 18; ofs += 2) {
|
||||
lov = take2(table[0]->patch_id + ofs);
|
||||
hiv = take2(table[nr-1]->patch_id + ofs);
|
||||
miv = take2(id + ofs);
|
||||
if (miv < lov)
|
||||
return -1;
|
||||
if (hiv < miv)
|
||||
return -1 - nr;
|
||||
if (lov != hiv) {
|
||||
/*
|
||||
* At this point miv could be equal
|
||||
* to hiv (but id could still be higher);
|
||||
* the invariant of (mi < hi) should be
|
||||
* kept.
|
||||
*/
|
||||
mi = (nr-1) * (miv - lov) / (hiv - lov);
|
||||
if (lo <= mi && mi < hi)
|
||||
break;
|
||||
die("oops");
|
||||
}
|
||||
}
|
||||
if (18 <= ofs)
|
||||
die("cannot happen -- lo and hi are identical");
|
||||
}
|
||||
|
||||
do {
|
||||
int cmp;
|
||||
cmp = hashcmp(table[mi]->patch_id, id);
|
||||
if (!cmp)
|
||||
return mi;
|
||||
if (cmp > 0)
|
||||
hi = mi;
|
||||
else
|
||||
lo = mi + 1;
|
||||
mi = (hi + lo) / 2;
|
||||
} while (lo < hi);
|
||||
return -lo-1;
|
||||
return sha1_pos(id, table, nr, patch_id_access);
|
||||
}
|
||||
|
||||
#define BUCKET_SIZE 190 /* 190 * 21 = 3990, with slop close enough to 4K */
|
||||
|
||||
35
quote.c
35
quote.c
@@ -72,7 +72,7 @@ void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
|
||||
}
|
||||
}
|
||||
|
||||
char *sq_dequote(char *arg)
|
||||
char *sq_dequote_step(char *arg, char **next)
|
||||
{
|
||||
char *dst = arg;
|
||||
char *src = arg;
|
||||
@@ -92,6 +92,8 @@ char *sq_dequote(char *arg)
|
||||
switch (*++src) {
|
||||
case '\0':
|
||||
*dst = 0;
|
||||
if (next)
|
||||
*next = NULL;
|
||||
return arg;
|
||||
case '\\':
|
||||
c = *++src;
|
||||
@@ -101,11 +103,40 @@ char *sq_dequote(char *arg)
|
||||
}
|
||||
/* Fallthrough */
|
||||
default:
|
||||
return NULL;
|
||||
if (!next || !isspace(*src))
|
||||
return NULL;
|
||||
do {
|
||||
c = *++src;
|
||||
} while (isspace(c));
|
||||
*dst = 0;
|
||||
*next = src;
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *sq_dequote(char *arg)
|
||||
{
|
||||
return sq_dequote_step(arg, NULL);
|
||||
}
|
||||
|
||||
int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
|
||||
{
|
||||
char *next = arg;
|
||||
|
||||
if (!*arg)
|
||||
return 0;
|
||||
do {
|
||||
char *dequoted = sq_dequote_step(next, &next);
|
||||
if (!dequoted)
|
||||
return -1;
|
||||
ALLOC_GROW(*argv, *nr + 1, *alloc);
|
||||
(*argv)[(*nr)++] = dequoted;
|
||||
} while (next);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 1 means: quote as octal
|
||||
* 0 means: quote as octal if (quote_path_fully)
|
||||
* -1 means: never quote
|
||||
|
||||
9
quote.h
9
quote.h
@@ -39,6 +39,15 @@ extern void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen);
|
||||
*/
|
||||
extern char *sq_dequote(char *);
|
||||
|
||||
/*
|
||||
* Same as the above, but can be used to unwrap many arguments in the
|
||||
* same string separated by space. "next" is changed to point to the
|
||||
* next argument that should be passed as first parameter. When there
|
||||
* is no more argument to be dequoted, "next" is updated to point to NULL.
|
||||
*/
|
||||
extern char *sq_dequote_step(char *arg, char **next);
|
||||
extern int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc);
|
||||
|
||||
extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
|
||||
extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq);
|
||||
extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
|
||||
|
||||
110
refs.c
110
refs.c
@@ -647,19 +647,24 @@ int for_each_ref(each_ref_fn fn, void *cb_data)
|
||||
return do_for_each_ref("refs/", fn, 0, 0, cb_data);
|
||||
}
|
||||
|
||||
int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
|
||||
{
|
||||
return do_for_each_ref(prefix, fn, strlen(prefix), 0, cb_data);
|
||||
}
|
||||
|
||||
int for_each_tag_ref(each_ref_fn fn, void *cb_data)
|
||||
{
|
||||
return do_for_each_ref("refs/tags/", fn, 10, 0, cb_data);
|
||||
return for_each_ref_in("refs/tags/", fn, cb_data);
|
||||
}
|
||||
|
||||
int for_each_branch_ref(each_ref_fn fn, void *cb_data)
|
||||
{
|
||||
return do_for_each_ref("refs/heads/", fn, 11, 0, cb_data);
|
||||
return for_each_ref_in("refs/heads/", fn, cb_data);
|
||||
}
|
||||
|
||||
int for_each_remote_ref(each_ref_fn fn, void *cb_data)
|
||||
{
|
||||
return do_for_each_ref("refs/remotes/", fn, 13, 0, cb_data);
|
||||
return for_each_ref_in("refs/remotes/", fn, cb_data);
|
||||
}
|
||||
|
||||
int for_each_rawref(each_ref_fn fn, void *cb_data)
|
||||
@@ -1652,3 +1657,102 @@ struct ref *find_ref_by_name(const struct ref *list, const char *name)
|
||||
return (struct ref *)list;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* generate a format suitable for scanf from a ref_rev_parse_rules
|
||||
* rule, that is replace the "%.*s" spec with a "%s" spec
|
||||
*/
|
||||
static void gen_scanf_fmt(char *scanf_fmt, const char *rule)
|
||||
{
|
||||
char *spec;
|
||||
|
||||
spec = strstr(rule, "%.*s");
|
||||
if (!spec || strstr(spec + 4, "%.*s"))
|
||||
die("invalid rule in ref_rev_parse_rules: %s", rule);
|
||||
|
||||
/* copy all until spec */
|
||||
strncpy(scanf_fmt, rule, spec - rule);
|
||||
scanf_fmt[spec - rule] = '\0';
|
||||
/* copy new spec */
|
||||
strcat(scanf_fmt, "%s");
|
||||
/* copy remaining rule */
|
||||
strcat(scanf_fmt, spec + 4);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
char *shorten_unambiguous_ref(const char *ref)
|
||||
{
|
||||
int i;
|
||||
static char **scanf_fmts;
|
||||
static int nr_rules;
|
||||
char *short_name;
|
||||
|
||||
/* pre generate scanf formats from ref_rev_parse_rules[] */
|
||||
if (!nr_rules) {
|
||||
size_t total_len = 0;
|
||||
|
||||
/* the rule list is NULL terminated, count them first */
|
||||
for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
|
||||
/* no +1 because strlen("%s") < strlen("%.*s") */
|
||||
total_len += strlen(ref_rev_parse_rules[nr_rules]);
|
||||
|
||||
scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
|
||||
|
||||
total_len = 0;
|
||||
for (i = 0; i < nr_rules; i++) {
|
||||
scanf_fmts[i] = (char *)&scanf_fmts[nr_rules]
|
||||
+ total_len;
|
||||
gen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);
|
||||
total_len += strlen(ref_rev_parse_rules[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* bail out if there are no rules */
|
||||
if (!nr_rules)
|
||||
return xstrdup(ref);
|
||||
|
||||
/* buffer for scanf result, at most ref must fit */
|
||||
short_name = xstrdup(ref);
|
||||
|
||||
/* skip first rule, it will always match */
|
||||
for (i = nr_rules - 1; i > 0 ; --i) {
|
||||
int j;
|
||||
int short_name_len;
|
||||
|
||||
if (1 != sscanf(ref, scanf_fmts[i], short_name))
|
||||
continue;
|
||||
|
||||
short_name_len = strlen(short_name);
|
||||
|
||||
/*
|
||||
* check if the short name resolves to a valid ref,
|
||||
* but use only rules prior to the matched one
|
||||
*/
|
||||
for (j = 0; j < i; j++) {
|
||||
const char *rule = ref_rev_parse_rules[j];
|
||||
unsigned char short_objectname[20];
|
||||
char refname[PATH_MAX];
|
||||
|
||||
/*
|
||||
* the short name is ambiguous, if it resolves
|
||||
* (with this previous rule) to a valid ref
|
||||
* read_ref() returns 0 on success
|
||||
*/
|
||||
mksnpath(refname, sizeof(refname),
|
||||
rule, short_name_len, short_name);
|
||||
if (!read_ref(refname, short_objectname))
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* short name is non-ambiguous if all previous rules
|
||||
* haven't resolved to a valid ref
|
||||
*/
|
||||
if (j == i)
|
||||
return short_name;
|
||||
}
|
||||
|
||||
free(short_name);
|
||||
return xstrdup(ref);
|
||||
}
|
||||
|
||||
2
refs.h
2
refs.h
@@ -20,6 +20,7 @@ struct ref_lock {
|
||||
typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
|
||||
extern int head_ref(each_ref_fn, void *);
|
||||
extern int for_each_ref(each_ref_fn, void *);
|
||||
extern int for_each_ref_in(const char *, each_ref_fn, void *);
|
||||
extern int for_each_tag_ref(each_ref_fn, void *);
|
||||
extern int for_each_branch_ref(each_ref_fn, void *);
|
||||
extern int for_each_remote_ref(each_ref_fn, void *);
|
||||
@@ -80,6 +81,7 @@ extern int for_each_reflog(each_ref_fn, void *);
|
||||
extern int check_ref_format(const char *target);
|
||||
|
||||
extern const char *prettify_ref(const struct ref *ref);
|
||||
extern char *shorten_unambiguous_ref(const char *ref);
|
||||
|
||||
/** rename ref, return 0 on success **/
|
||||
extern int rename_ref(const char *oldref, const char *newref, const char *logmsg);
|
||||
|
||||
11
remote.c
11
remote.c
@@ -667,6 +667,17 @@ struct remote *remote_get(const char *name)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int remote_is_configured(const char *name)
|
||||
{
|
||||
int i;
|
||||
read_config();
|
||||
|
||||
for (i = 0; i < remotes_nr; i++)
|
||||
if (!strcmp(name, remotes[i]->name))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int for_each_remote(each_remote_fn fn, void *priv)
|
||||
{
|
||||
int i, result = 0;
|
||||
|
||||
1
remote.h
1
remote.h
@@ -45,6 +45,7 @@ struct remote {
|
||||
};
|
||||
|
||||
struct remote *remote_get(const char *name);
|
||||
int remote_is_configured(const char *name);
|
||||
|
||||
typedef int each_remote_fn(struct remote *remote, void *priv);
|
||||
int for_each_remote(each_remote_fn fn, void *priv);
|
||||
|
||||
101
sha1-lookup.c
101
sha1-lookup.c
@@ -1,6 +1,107 @@
|
||||
#include "cache.h"
|
||||
#include "sha1-lookup.h"
|
||||
|
||||
static uint32_t take2(const unsigned char *sha1)
|
||||
{
|
||||
return ((sha1[0] << 8) | sha1[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Conventional binary search loop looks like this:
|
||||
*
|
||||
* do {
|
||||
* int mi = (lo + hi) / 2;
|
||||
* int cmp = "entry pointed at by mi" minus "target";
|
||||
* if (!cmp)
|
||||
* return (mi is the wanted one)
|
||||
* if (cmp > 0)
|
||||
* hi = mi; "mi is larger than target"
|
||||
* else
|
||||
* lo = mi+1; "mi is smaller than target"
|
||||
* } while (lo < hi);
|
||||
*
|
||||
* The invariants are:
|
||||
*
|
||||
* - When entering the loop, lo points at a slot that is never
|
||||
* above the target (it could be at the target), hi points at a
|
||||
* slot that is guaranteed to be above the target (it can never
|
||||
* be at the target).
|
||||
*
|
||||
* - We find a point 'mi' between lo and hi (mi could be the same
|
||||
* as lo, but never can be the same as hi), and check if it hits
|
||||
* the target. There are three cases:
|
||||
*
|
||||
* - if it is a hit, we are happy.
|
||||
*
|
||||
* - if it is strictly higher than the target, we update hi with
|
||||
* it.
|
||||
*
|
||||
* - if it is strictly lower than the target, we update lo to be
|
||||
* one slot after it, because we allow lo to be at the target.
|
||||
*
|
||||
* When choosing 'mi', we do not have to take the "middle" but
|
||||
* anywhere in between lo and hi, as long as lo <= mi < hi is
|
||||
* satisfied. When we somehow know that the distance between the
|
||||
* target and lo is much shorter than the target and hi, we could
|
||||
* pick mi that is much closer to lo than the midway.
|
||||
*/
|
||||
/*
|
||||
* The table should contain "nr" elements.
|
||||
* The sha1 of element i (between 0 and nr - 1) should be returned
|
||||
* by "fn(i, table)".
|
||||
*/
|
||||
int sha1_pos(const unsigned char *sha1, void *table, size_t nr,
|
||||
sha1_access_fn fn)
|
||||
{
|
||||
size_t hi = nr;
|
||||
size_t lo = 0;
|
||||
size_t mi = 0;
|
||||
|
||||
if (!nr)
|
||||
return -1;
|
||||
|
||||
if (nr != 1) {
|
||||
size_t lov, hiv, miv, ofs;
|
||||
|
||||
for (ofs = 0; ofs < 18; ofs += 2) {
|
||||
lov = take2(fn(0, table) + ofs);
|
||||
hiv = take2(fn(nr - 1, table) + ofs);
|
||||
miv = take2(sha1 + ofs);
|
||||
if (miv < lov)
|
||||
return -1;
|
||||
if (hiv < miv)
|
||||
return -1 - nr;
|
||||
if (lov != hiv) {
|
||||
/*
|
||||
* At this point miv could be equal
|
||||
* to hiv (but sha1 could still be higher);
|
||||
* the invariant of (mi < hi) should be
|
||||
* kept.
|
||||
*/
|
||||
mi = (nr - 1) * (miv - lov) / (hiv - lov);
|
||||
if (lo <= mi && mi < hi)
|
||||
break;
|
||||
die("BUG: assertion failed in binary search");
|
||||
}
|
||||
}
|
||||
if (18 <= ofs)
|
||||
die("cannot happen -- lo and hi are identical");
|
||||
}
|
||||
|
||||
do {
|
||||
int cmp;
|
||||
cmp = hashcmp(fn(mi, table), sha1);
|
||||
if (!cmp)
|
||||
return mi;
|
||||
if (cmp > 0)
|
||||
hi = mi;
|
||||
else
|
||||
lo = mi + 1;
|
||||
mi = (hi + lo) / 2;
|
||||
} while (lo < hi);
|
||||
return -lo-1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Conventional binary search loop looks like this:
|
||||
*
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
#ifndef SHA1_LOOKUP_H
|
||||
#define SHA1_LOOKUP_H
|
||||
|
||||
typedef const unsigned char *sha1_access_fn(size_t index, void *table);
|
||||
|
||||
extern int sha1_pos(const unsigned char *sha1,
|
||||
void *table,
|
||||
size_t nr,
|
||||
sha1_access_fn fn);
|
||||
|
||||
extern int sha1_entry_pos(const void *table,
|
||||
size_t elem_size,
|
||||
size_t key_offset,
|
||||
|
||||
@@ -8,6 +8,10 @@ then
|
||||
say 'skipping git svn tests, NO_SVN_TESTS defined'
|
||||
test_done
|
||||
fi
|
||||
if ! test_have_prereq PERL; then
|
||||
say 'skipping git svn tests, perl not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
GIT_DIR=$PWD/.git
|
||||
GIT_SVN_DIR=$GIT_DIR/svn/git-svn
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
test_description='add -i basic tests'
|
||||
. ./test-lib.sh
|
||||
|
||||
if ! test_have_prereq PERL; then
|
||||
say 'skipping git add -i tests, perl not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
test_expect_success 'setup (initial)' '
|
||||
echo content >file &&
|
||||
git add file &&
|
||||
|
||||
81
t/t5506-remote-groups.sh
Executable file
81
t/t5506-remote-groups.sh
Executable file
@@ -0,0 +1,81 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git remote group handling'
|
||||
. ./test-lib.sh
|
||||
|
||||
mark() {
|
||||
echo "$1" >mark
|
||||
}
|
||||
|
||||
update_repo() {
|
||||
(cd $1 &&
|
||||
echo content >>file &&
|
||||
git add file &&
|
||||
git commit -F ../mark)
|
||||
}
|
||||
|
||||
update_repos() {
|
||||
update_repo one $1 &&
|
||||
update_repo two $1
|
||||
}
|
||||
|
||||
repo_fetched() {
|
||||
if test "`git log -1 --pretty=format:%s $1 --`" = "`cat mark`"; then
|
||||
echo >&2 "repo was fetched: $1"
|
||||
return 0
|
||||
fi
|
||||
echo >&2 "repo was not fetched: $1"
|
||||
return 1
|
||||
}
|
||||
|
||||
test_expect_success 'setup' '
|
||||
mkdir one && (cd one && git init) &&
|
||||
mkdir two && (cd two && git init) &&
|
||||
git remote add -m master one one &&
|
||||
git remote add -m master two two
|
||||
'
|
||||
|
||||
test_expect_success 'no group updates all' '
|
||||
mark update-all &&
|
||||
update_repos &&
|
||||
git remote update &&
|
||||
repo_fetched one &&
|
||||
repo_fetched two
|
||||
'
|
||||
|
||||
test_expect_success 'nonexistant group produces error' '
|
||||
mark nonexistant &&
|
||||
update_repos &&
|
||||
test_must_fail git remote update nonexistant &&
|
||||
! repo_fetched one &&
|
||||
! repo_fetched two
|
||||
'
|
||||
|
||||
test_expect_success 'updating group updates all members' '
|
||||
mark group-all &&
|
||||
update_repos &&
|
||||
git config --add remotes.all one &&
|
||||
git config --add remotes.all two &&
|
||||
git remote update all &&
|
||||
repo_fetched one &&
|
||||
repo_fetched two
|
||||
'
|
||||
|
||||
test_expect_success 'updating group does not update non-members' '
|
||||
mark group-some &&
|
||||
update_repos &&
|
||||
git config --add remotes.some one &&
|
||||
git remote update some &&
|
||||
repo_fetched one &&
|
||||
! repo_fetched two
|
||||
'
|
||||
|
||||
test_expect_success 'updating remote name updates that remote' '
|
||||
mark remote-name &&
|
||||
update_repos &&
|
||||
git remote update one &&
|
||||
repo_fetched one &&
|
||||
! repo_fetched two
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -506,6 +506,66 @@ test_expect_success 'optimized merge base checks' '
|
||||
unset GIT_TRACE
|
||||
'
|
||||
|
||||
# This creates another side branch called "parallel" with some files
|
||||
# in some directories, to test bisecting with paths.
|
||||
#
|
||||
# We should have the following:
|
||||
#
|
||||
# P1-P2-P3-P4-P5-P6-P7
|
||||
# / / /
|
||||
# H1-H2-H3-H4-H5-H6-H7
|
||||
# \ \ \
|
||||
# S5-A \
|
||||
# \ \
|
||||
# S6-S7----B
|
||||
#
|
||||
test_expect_success '"parallel" side branch creation' '
|
||||
git bisect reset &&
|
||||
git checkout -b parallel $HASH1 &&
|
||||
mkdir dir1 dir2 &&
|
||||
add_line_into_file "1(para): line 1 on parallel branch" dir1/file1 &&
|
||||
PARA_HASH1=$(git rev-parse --verify HEAD) &&
|
||||
add_line_into_file "2(para): line 2 on parallel branch" dir2/file2 &&
|
||||
PARA_HASH2=$(git rev-parse --verify HEAD) &&
|
||||
add_line_into_file "3(para): line 3 on parallel branch" dir2/file3 &&
|
||||
PARA_HASH3=$(git rev-parse --verify HEAD)
|
||||
git merge -m "merge HASH4 and PARA_HASH3" "$HASH4" &&
|
||||
PARA_HASH4=$(git rev-parse --verify HEAD)
|
||||
add_line_into_file "5(para): add line on parallel branch" dir1/file1 &&
|
||||
PARA_HASH5=$(git rev-parse --verify HEAD)
|
||||
add_line_into_file "6(para): add line on parallel branch" dir2/file2 &&
|
||||
PARA_HASH6=$(git rev-parse --verify HEAD)
|
||||
git merge -m "merge HASH7 and PARA_HASH6" "$HASH7" &&
|
||||
PARA_HASH7=$(git rev-parse --verify HEAD)
|
||||
'
|
||||
|
||||
test_expect_success 'restricting bisection on one dir' '
|
||||
git bisect reset &&
|
||||
git bisect start HEAD $HASH1 -- dir1 &&
|
||||
para1=$(git rev-parse --verify HEAD) &&
|
||||
test "$para1" = "$PARA_HASH1" &&
|
||||
git bisect bad > my_bisect_log.txt &&
|
||||
grep "$PARA_HASH1 is first bad commit" my_bisect_log.txt
|
||||
'
|
||||
|
||||
test_expect_success 'restricting bisection on one dir and a file' '
|
||||
git bisect reset &&
|
||||
git bisect start HEAD $HASH1 -- dir1 hello &&
|
||||
para4=$(git rev-parse --verify HEAD) &&
|
||||
test "$para4" = "$PARA_HASH4" &&
|
||||
git bisect bad &&
|
||||
hash3=$(git rev-parse --verify HEAD) &&
|
||||
test "$hash3" = "$HASH3" &&
|
||||
git bisect good &&
|
||||
hash4=$(git rev-parse --verify HEAD) &&
|
||||
test "$hash4" = "$HASH4" &&
|
||||
git bisect good &&
|
||||
para1=$(git rev-parse --verify HEAD) &&
|
||||
test "$para1" = "$PARA_HASH1" &&
|
||||
git bisect good > my_bisect_log.txt &&
|
||||
grep "$PARA_HASH4 is first bad commit" my_bisect_log.txt
|
||||
'
|
||||
|
||||
#
|
||||
#
|
||||
test_done
|
||||
|
||||
@@ -26,6 +26,13 @@ test_expect_success 'Create sample commit with known timestamp' '
|
||||
git tag -a -m "Tagging at $datestamp" testtag
|
||||
'
|
||||
|
||||
test_expect_success 'Create upstream config' '
|
||||
git update-ref refs/remotes/origin/master master &&
|
||||
git remote add origin nowhere &&
|
||||
git config branch.master.remote origin &&
|
||||
git config branch.master.merge refs/heads/master
|
||||
'
|
||||
|
||||
test_atom() {
|
||||
case "$1" in
|
||||
head) ref=refs/heads/master ;;
|
||||
@@ -39,6 +46,7 @@ test_atom() {
|
||||
}
|
||||
|
||||
test_atom head refname refs/heads/master
|
||||
test_atom head upstream refs/remotes/origin/master
|
||||
test_atom head objecttype commit
|
||||
test_atom head objectsize 171
|
||||
test_atom head objectname 67a36f10722846e891fbada1ba48ed035de75581
|
||||
@@ -68,6 +76,7 @@ test_atom head contents 'Initial
|
||||
'
|
||||
|
||||
test_atom tag refname refs/tags/testtag
|
||||
test_atom tag upstream ''
|
||||
test_atom tag objecttype tag
|
||||
test_atom tag objectsize 154
|
||||
test_atom tag objectname 98b46b1d36e5b07909de1b3886224e3e81e87322
|
||||
@@ -203,6 +212,7 @@ test_expect_success 'Check format "rfc2822" date fields output' '
|
||||
|
||||
cat >expected <<\EOF
|
||||
refs/heads/master
|
||||
refs/remotes/origin/master
|
||||
refs/tags/testtag
|
||||
EOF
|
||||
|
||||
@@ -214,6 +224,7 @@ test_expect_success 'Verify ascending sort' '
|
||||
|
||||
cat >expected <<\EOF
|
||||
refs/tags/testtag
|
||||
refs/remotes/origin/master
|
||||
refs/heads/master
|
||||
EOF
|
||||
|
||||
@@ -224,6 +235,7 @@ test_expect_success 'Verify descending sort' '
|
||||
|
||||
cat >expected <<\EOF
|
||||
'refs/heads/master'
|
||||
'refs/remotes/origin/master'
|
||||
'refs/tags/testtag'
|
||||
EOF
|
||||
|
||||
@@ -244,6 +256,7 @@ test_expect_success 'Quoting style: python' '
|
||||
|
||||
cat >expected <<\EOF
|
||||
"refs/heads/master"
|
||||
"refs/remotes/origin/master"
|
||||
"refs/tags/testtag"
|
||||
EOF
|
||||
|
||||
@@ -273,6 +286,15 @@ test_expect_success 'Check short refname format' '
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
cat >expected <<EOF
|
||||
origin/master
|
||||
EOF
|
||||
|
||||
test_expect_success 'Check short upstream format' '
|
||||
git for-each-ref --format="%(upstream:short)" refs/heads >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'Check for invalid refname format' '
|
||||
test_must_fail git for-each-ref --format="%(refname:INVALID)"
|
||||
'
|
||||
|
||||
@@ -38,7 +38,7 @@ test_expect_success \
|
||||
"echo King of the bongo >file &&
|
||||
test_must_fail git commit -m foo -a file"
|
||||
|
||||
test_expect_success \
|
||||
test_expect_success PERL \
|
||||
"using paths with --interactive" \
|
||||
"echo bong-o-bong >file &&
|
||||
! (echo 7 | git commit -m foo --interactive file)"
|
||||
@@ -119,7 +119,7 @@ test_expect_success \
|
||||
"echo 'gak' >file && \
|
||||
git commit -m 'author' --author 'Rubber Duck <rduck@convoy.org>' -a"
|
||||
|
||||
test_expect_success \
|
||||
test_expect_success PERL \
|
||||
"interactive add" \
|
||||
"echo 7 | git commit --interactive | grep 'What now'"
|
||||
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
test_description='git send-email'
|
||||
. ./test-lib.sh
|
||||
|
||||
if ! test_have_prereq PERL; then
|
||||
say 'skipping git send-email tests, perl not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
PROG='git send-email'
|
||||
test_expect_success \
|
||||
'prepare reference tree' \
|
||||
|
||||
@@ -31,6 +31,22 @@ test_expect_success 'clone an SVN repository with ignored www directory' '
|
||||
test_cmp expect expect2
|
||||
'
|
||||
|
||||
test_expect_success 'init+fetch an SVN repository with ignored www directory' '
|
||||
git svn init "$svnrepo" c &&
|
||||
( cd c && git svn fetch --ignore-paths="^www" ) &&
|
||||
rm expect2 &&
|
||||
echo test_qqq > expect &&
|
||||
for i in c/*/*.txt; do cat $i >> expect2; done &&
|
||||
test_cmp expect expect2
|
||||
'
|
||||
|
||||
test_expect_success 'verify ignore-paths config saved by clone' '
|
||||
(
|
||||
cd g &&
|
||||
git config --get svn-remote.svn.ignore-paths | fgrep "www"
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'SVN-side change outside of www' '
|
||||
(
|
||||
cd s &&
|
||||
@@ -41,9 +57,20 @@ test_expect_success 'SVN-side change outside of www' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'update git svn-cloned repo' '
|
||||
test_expect_success 'update git svn-cloned repo (config ignore)' '
|
||||
(
|
||||
cd g &&
|
||||
git svn rebase &&
|
||||
printf "test_qqq\nb\n" > expect &&
|
||||
for i in */*.txt; do cat $i >> expect2; done &&
|
||||
test_cmp expect2 expect &&
|
||||
rm expect expect2
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'update git svn-cloned repo (option ignore)' '
|
||||
(
|
||||
cd c &&
|
||||
git svn rebase --ignore-paths="^www" &&
|
||||
printf "test_qqq\nb\n" > expect &&
|
||||
for i in */*.txt; do cat $i >> expect2; done &&
|
||||
@@ -62,9 +89,20 @@ test_expect_success 'SVN-side change inside of ignored www' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'update git svn-cloned repo' '
|
||||
test_expect_success 'update git svn-cloned repo (config ignore)' '
|
||||
(
|
||||
cd g &&
|
||||
git svn rebase &&
|
||||
printf "test_qqq\nb\n" > expect &&
|
||||
for i in */*.txt; do cat $i >> expect2; done &&
|
||||
test_cmp expect2 expect &&
|
||||
rm expect expect2
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'update git svn-cloned repo (option ignore)' '
|
||||
(
|
||||
cd c &&
|
||||
git svn rebase --ignore-paths="^www" &&
|
||||
printf "test_qqq\nb\n" > expect &&
|
||||
for i in */*.txt; do cat $i >> expect2; done &&
|
||||
@@ -84,9 +122,20 @@ test_expect_success 'SVN-side change in and out of ignored www' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'update git svn-cloned repo again' '
|
||||
test_expect_success 'update git svn-cloned repo again (config ignore)' '
|
||||
(
|
||||
cd g &&
|
||||
git svn rebase &&
|
||||
printf "test_qqq\nb\nygg\n" > expect &&
|
||||
for i in */*.txt; do cat $i >> expect2; done &&
|
||||
test_cmp expect2 expect &&
|
||||
rm expect expect2
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'update git svn-cloned repo again (option ignore)' '
|
||||
(
|
||||
cd c &&
|
||||
git svn rebase --ignore-paths="^www" &&
|
||||
printf "test_qqq\nb\nygg\n" > expect &&
|
||||
for i in */*.txt; do cat $i >> expect2; done &&
|
||||
|
||||
@@ -6,6 +6,11 @@ test_description='Test export of commits to CVS'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
if ! test_have_prereq PERL; then
|
||||
say 'skipping git cvsexportcommit tests, perl not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
cvs >/dev/null 2>&1
|
||||
if test $? -ne 1
|
||||
then
|
||||
|
||||
@@ -10,6 +10,10 @@ cvs CLI client via git-cvsserver server'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
if ! test_have_prereq PERL; then
|
||||
say 'skipping git cvsserver tests, perl not available'
|
||||
test_done
|
||||
fi
|
||||
cvs >/dev/null 2>&1
|
||||
if test $? -ne 1
|
||||
then
|
||||
|
||||
@@ -52,6 +52,11 @@ then
|
||||
say 'skipping git-cvsserver tests, cvs not found'
|
||||
test_done
|
||||
fi
|
||||
if ! test_have_prereq PERL
|
||||
then
|
||||
say 'skipping git-cvsserver tests, perl not available'
|
||||
test_done
|
||||
fi
|
||||
perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
|
||||
say 'skipping git-cvsserver tests, Perl SQLite interface unavailable'
|
||||
test_done
|
||||
|
||||
@@ -65,6 +65,11 @@ gitweb_run () {
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
if ! test_have_prereq PERL; then
|
||||
say 'skipping gitweb tests, perl not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
|
||||
say 'skipping gitweb tests, perl version is too old'
|
||||
test_done
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
test_description='git cvsimport basic tests'
|
||||
. ./test-lib.sh
|
||||
|
||||
if ! test_have_prereq PERL; then
|
||||
say 'skipping git cvsimport tests, perl not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
CVSROOT=$(pwd)/cvsroot
|
||||
export CVSROOT
|
||||
unset CVS_SERVER
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
test_description='perl interface (Git.pm)'
|
||||
. ./test-lib.sh
|
||||
|
||||
if ! test_have_prereq PERL; then
|
||||
say 'skipping perl interface tests, perl not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
perl -MTest::More -e 0 2>/dev/null || {
|
||||
say "Perl Test::More unavailable, skipping test"
|
||||
test_done
|
||||
|
||||
@@ -698,6 +698,8 @@ case $(uname -s) in
|
||||
;;
|
||||
esac
|
||||
|
||||
test -z "$NO_PERL" && test_set_prereq PERL
|
||||
|
||||
# test whether the filesystem supports symbolic links
|
||||
ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS
|
||||
rm -f y
|
||||
|
||||
4
unimplemented.sh
Normal file
4
unimplemented.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo >&2 "fatal: git was built without support for `basename $0` (@@REASON@@)."
|
||||
exit 128
|
||||
@@ -66,7 +66,7 @@ static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
|
||||
}
|
||||
|
||||
static FILE *pack_pipe = NULL;
|
||||
static void show_commit(struct commit *commit)
|
||||
static void show_commit(struct commit *commit, void *data)
|
||||
{
|
||||
if (commit->object.flags & BOUNDARY)
|
||||
fputc('-', pack_pipe);
|
||||
@@ -78,7 +78,7 @@ static void show_commit(struct commit *commit)
|
||||
commit->buffer = NULL;
|
||||
}
|
||||
|
||||
static void show_object(struct object_array_entry *p)
|
||||
static void show_object(struct object_array_entry *p, void *data)
|
||||
{
|
||||
/* An object with name "foo\n0000000..." can be used to
|
||||
* confuse downstream git-pack-objects very badly.
|
||||
@@ -134,7 +134,7 @@ static int do_rev_list(int fd, void *create_full_pack)
|
||||
if (prepare_revision_walk(&revs))
|
||||
die("revision walk setup failed");
|
||||
mark_edges_uninteresting(revs.commits, &revs, show_edge);
|
||||
traverse_commit_list(&revs, show_commit, show_object);
|
||||
traverse_commit_list(&revs, show_commit, show_object, NULL);
|
||||
fflush(pack_pipe);
|
||||
fclose(pack_pipe);
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user