Merge branch 'jc/merge-base' (early part) into next

* 'jc/merge-base' (early part):
  in_merge_bases(): optimization
  merge_base(): move traversal into a separate function.
  Allow in_merge_bases() to take more than one reference commits.
  Make merge-base a built-in.
  Don't die in git-http-fetch when fetching packs.
  Update git-svn manpage to remove the implication that SVN::* is optional.
  Replacing the system call pread() with lseek()/xread()/lseek() sequence.
  gitweb: Fix git_patchset_body not closing <div class="patch">
  git.el: Define the propertize function if needed, for XEmacs compatibility.
  git-clone: Make sure the master branch exists before running cat on it.
  git-apply: Remove directories that have become empty after deleting a file.
  get_tree_entry: map blank requested entry to tree root
  builtin-archive: do not free a tree held by the object layer.
  Fix "Do not ignore a detected patchfile brokenness."
  Do not ignore a detected patchfile brokenness.
This commit is contained in:
Junio C Hamano
2007-01-09 20:41:53 -08:00
19 changed files with 147 additions and 110 deletions

View File

@@ -249,8 +249,7 @@ repo-config key: svn.authorsfile
-q::
--quiet::
Make git-svn less verbose. This only affects git-svn if you
have the SVN::* libraries installed and are using them.
Make git-svn less verbose.
--repack[=<n>]::
--repack-flags=<flags>
@@ -321,8 +320,6 @@ for more information on using GIT_SVN_ID.
started tracking a branch and never tracked the trunk it was
descended from.
This relies on the SVN::* libraries to work.
repo-config key: svn.followparent
--no-metadata::
@@ -350,25 +347,6 @@ Run this if you used an old version of git-svn that used
"git-svn-HEAD" instead of "remotes/git-svn" as the branch
for tracking the remote.
--no-ignore-externals::
Only used with the 'fetch' and 'rebuild' command.
This command has no effect when you are using the SVN::*
libraries with git, svn:externals are always avoided.
By default, git-svn passes --ignore-externals to svn to avoid
fetching svn:external trees into git. Pass this flag to enable
externals tracking directly via git.
Versions of svn that do not support --ignore-externals are
automatically detected and this flag will be automatically
enabled for them.
Otherwise, do not enable this flag unless you know what you're
doing.
repo-config key: svn.noignoreexternals
--ignore-nodate::
Only used with the 'fetch' command.
@@ -486,49 +464,18 @@ This allows you to tie unfetched SVN revision 375 to your current HEAD:
git-svn fetch 375=$(git-rev-parse HEAD)
------------------------------------------------
Advanced Example: Tracking a Reorganized Repository
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Note: this example is now obsolete if you have SVN::* libraries
installed. Simply use --follow-parent when fetching.
If you're tracking a directory that has moved, or otherwise been
branched or tagged off of another directory in the repository and you
care about the full history of the project, then you can read this
section.
care about the full history of the project, then you can use
the --follow-parent option.
This is how Yann Dirson tracked the trunk of the ufoai directory when
the /trunk directory of his repository was moved to /ufoai/trunk and
he needed to continue tracking /ufoai/trunk where /trunk left off.
------------------------------------------------------------------------
# This log message shows when the repository was reorganized:
r166 | ydirson | 2006-03-02 01:36:55 +0100 (Thu, 02 Mar 2006) | 1 line
Changed paths:
D /trunk
A /ufoai/trunk (from /trunk:165)
# First we start tracking the old revisions:
GIT_SVN_ID=git-oldsvn git-svn init \
https://svn.sourceforge.net/svnroot/ufoai/trunk
GIT_SVN_ID=git-oldsvn git-svn fetch -r1:165
# And now, we continue tracking the new revisions:
GIT_SVN_ID=git-newsvn git-svn init \
https://svn.sourceforge.net/svnroot/ufoai/ufoai/trunk
GIT_SVN_ID=git-newsvn git-svn fetch \
166=`git-rev-parse refs/remotes/git-oldsvn`
------------------------------------------------------------------------
------------------------------------------------
git-svn fetch --follow-parent
------------------------------------------------
BUGS
----
If you are not using the SVN::* Perl libraries and somebody commits a
conflicting changeset to SVN at a bad moment (right before you commit)
causing a conflict and your commit to fail, your svn working tree
($GIT_DIR/git-svn/tree) may be dirtied. The easiest thing to do is
probably just to rm -rf $GIT_DIR/git-svn/tree and run 'rebuild'. You
can avoid this problem entirely by using 'dcommit'.
We ignore all SVN properties except svn:executable. Too difficult to
map them since we rely heavily on git write-tree being _exactly_ the
same on both the SVN and git working trees and I prefer not to clutter

View File

@@ -69,6 +69,9 @@ all:
#
# Define NO_MMAP if you want to avoid mmap.
#
# Define NO_PREAD if you have a problem with pread() system call (e.g.
# cygwin.dll before v1.5.22).
#
# Define NO_FAST_WORKING_DIRECTORY if accessing objects in pack files is
# generally faster on your platform than accessing the working directory.
#
@@ -191,7 +194,6 @@ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
PROGRAMS = \
git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \
git-hash-object$X git-index-pack$X git-local-fetch$X \
git-merge-base$X \
git-daemon$X \
git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
git-peek-remote$X git-receive-pack$X \
@@ -286,6 +288,7 @@ BUILTIN_OBJS = \
builtin-ls-tree.o \
builtin-mailinfo.o \
builtin-mailsplit.o \
builtin-merge-base.o \
builtin-merge-file.o \
builtin-mv.o \
builtin-name-rev.o \
@@ -523,6 +526,10 @@ ifdef NO_MMAP
COMPAT_CFLAGS += -DNO_MMAP
COMPAT_OBJS += compat/mmap.o
endif
ifdef NO_PREAD
COMPAT_CFLAGS += -DNO_PREAD
COMPAT_OBJS += compat/pread.o
endif
ifdef NO_FAST_WORKING_DIRECTORY
BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
endif

View File

@@ -811,7 +811,8 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
struct fragment dummy;
if (parse_fragment_header(line, len, &dummy) < 0)
continue;
error("patch fragment without header at line %d: %.*s", linenr, (int)len-1, line);
die("patch fragment without header at line %d: %.*s",
linenr, (int)len-1, line);
}
if (size < len + 6)
@@ -2238,8 +2239,19 @@ static void remove_file(struct patch *patch)
die("unable to remove %s from index", patch->old_name);
cache_tree_invalidate_path(active_cache_tree, patch->old_name);
}
if (!cached)
unlink(patch->old_name);
if (!cached) {
if (!unlink(patch->old_name)) {
char *name = xstrdup(patch->old_name);
char *end = strrchr(name, '/');
while (end) {
*end = 0;
if (rmdir(name))
break;
end = strrchr(name, '/');
}
free(name);
}
}
}
static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size)

View File

@@ -137,7 +137,6 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
if (err || !S_ISDIR(mode))
die("current working directory is untracked");
free(tree);
tree = parse_tree_indirect(tree_sha1);
}
ar_args->tree = tree;

View File

@@ -134,7 +134,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
*/
if (!force &&
!in_merge_bases(rev, head_rev)) {
!in_merge_bases(rev, &head_rev, 1)) {
error("The branch '%s' is not a strict subset of "
"your current HEAD.\n"
"If you are sure you want to delete it, "

View File

@@ -1,9 +1,7 @@
#include "cache.h"
#include "commit.h"
static int show_all;
static int merge_base(struct commit *rev1, struct commit *rev2)
static int show_merge_base(struct commit *rev1, struct commit *rev2, int show_all)
{
struct commit_list *result = get_merge_bases(rev1, rev2, 0);
@@ -23,16 +21,16 @@ static int merge_base(struct commit *rev1, struct commit *rev2)
static const char merge_base_usage[] =
"git-merge-base [--all] <commit-id> <commit-id>";
int main(int argc, char **argv)
int cmd_merge_base(int argc, const char **argv, const char *prefix)
{
struct commit *rev1, *rev2;
unsigned char rev1key[20], rev2key[20];
int show_all = 0;
setup_git_directory();
git_config(git_default_config);
while (1 < argc && argv[1][0] == '-') {
char *arg = argv[1];
const char *arg = argv[1];
if (!strcmp(arg, "-a") || !strcmp(arg, "--all"))
show_all = 1;
else
@@ -49,5 +47,5 @@ int main(int argc, char **argv)
rev2 = lookup_commit_reference(rev2key);
if (!rev1 || !rev2)
return 1;
return merge_base(rev1, rev2);
return show_merge_base(rev1, rev2, show_all);
}

View File

@@ -210,8 +210,8 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
if ((timestamp < cb->cmd->expire_unreachable) &&
(!cb->ref_commit ||
(old && !in_merge_bases(old, cb->ref_commit)) ||
(new && !in_merge_bases(new, cb->ref_commit))))
(old && !in_merge_bases(old, &cb->ref_commit, 1)) ||
(new && !in_merge_bases(new, &cb->ref_commit, 1))))
goto prune;
if (cb->newlog) {

View File

@@ -42,6 +42,7 @@ extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
extern int cmd_mv(int argc, const char **argv, const char *prefix);
extern int cmd_name_rev(int argc, const char **argv, const char *prefix);

View File

@@ -1034,25 +1034,10 @@ static struct commit *interesting(struct commit_list *list)
return NULL;
}
static struct commit_list *merge_bases(struct commit *one, struct commit *two)
static struct commit_list *base_traverse(struct commit_list *list, struct commit *stop)
{
struct commit_list *list = NULL;
struct commit_list *result = NULL;
if (one == two)
/* We do not mark this even with RESULT so we do not
* have to clean it up.
*/
return commit_list_insert(one, &result);
parse_commit(one);
parse_commit(two);
one->object.flags |= PARENT1;
two->object.flags |= PARENT2;
insert_by_date(one, &list);
insert_by_date(two, &list);
while (interesting(list)) {
struct commit *commit;
struct commit_list *parents;
@@ -1083,10 +1068,20 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)
p->object.flags |= flags;
insert_by_date(p, &list);
}
if (stop && (stop->object.flags & PARENT2)) {
free_commit_list(list);
list = NULL;
insert_by_date(stop, &list);
return list;
}
}
/* Clean up the result to remove stale ones */
free_commit_list(list);
if (stop)
return NULL;
list = result; result = NULL;
while (list) {
struct commit_list *n = list->next;
@@ -1098,6 +1093,27 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)
return result;
}
static struct commit_list *merge_bases(struct commit *one, struct commit *two)
{
struct commit_list *list = NULL;
if (one == two)
/* We do not mark this even with RESULT so we do not
* have to clean it up.
*/
return commit_list_insert(one, &list);
parse_commit(one);
parse_commit(two);
one->object.flags |= PARENT1;
two->object.flags |= PARENT2;
insert_by_date(one, &list);
insert_by_date(two, &list);
return base_traverse(list, NULL);
}
struct commit_list *get_merge_bases(struct commit *one,
struct commit *two,
int cleanup)
@@ -1158,19 +1174,32 @@ struct commit_list *get_merge_bases(struct commit *one,
return result;
}
int in_merge_bases(struct commit *rev1, struct commit *rev2)
int in_merge_bases(struct commit *commit, struct commit **reference, int num)
{
struct commit_list *bases, *b;
int ret = 0;
struct commit_list *result, *list;
int i, ret;
bases = get_merge_bases(rev1, rev2, 1);
for (b = bases; b; b = b->next) {
if (!hashcmp(rev1->object.sha1, b->item->object.sha1)) {
ret = 1;
break;
list = NULL;
parse_commit(commit);
commit->object.flags |= PARENT1;
insert_by_date(commit, &list);
for (i = 0; i < num; i++) {
struct commit *two = reference[i];
parse_commit(two);
if (!(two->object.flags & PARENT2)) {
two->object.flags |= PARENT2;
insert_by_date(two, &list);
}
}
result = base_traverse(list, commit);
ret = !!result;
free_commit_list(result);
free_commit_list(bases);
clear_commit_marks(commit, all_flags);
for (i = 0; i < num; i++) {
struct commit *two = reference[i];
clear_commit_marks(two, all_flags);
}
return ret;
}

View File

@@ -114,5 +114,5 @@ extern int is_repository_shallow();
extern struct commit_list *get_shallow_commits(struct object_array *heads,
int depth, int shallow_flag, int not_shallow_flag);
int in_merge_bases(struct commit *rev1, struct commit *rev2);
int in_merge_bases(struct commit *, struct commit **, int);
#endif /* COMMIT_H */

18
compat/pread.c Normal file
View File

@@ -0,0 +1,18 @@
#include "../git-compat-util.h"
ssize_t git_pread(int fd, void *buf, size_t count, off_t offset)
{
off_t current_offset;
ssize_t rc;
current_offset = lseek(fd, 0, SEEK_CUR);
if (lseek(fd, offset, SEEK_SET) < 0)
return -1;
rc = read_in_full(fd, buf, count);
if (current_offset != lseek(fd, current_offset, SEEK_SET))
return -1;
return rc;
}

View File

@@ -280,6 +280,15 @@ and returns the process output as a string."
(git-run-command nil nil "update-index" "--info-only" "--add" "--" (file-relative-name ignore-name)))
(git-add-status-file (if created 'added 'modified) (file-relative-name ignore-name))))
; propertize definition for XEmacs, stolen from erc-compat
(eval-when-compile
(unless (fboundp 'propertize)
(defun propertize (string &rest props)
(let ((string (copy-sequence string)))
(while props
(put-text-property 0 (length string) (nth 0 props) (nth 1 props) string)
(setq props (cddr props)))
string))))
;;;; Wrappers for basic git commands
;;;; ------------------------------------------------------------
@@ -448,11 +457,10 @@ and returns the process output as a string."
(defun git-fileinfo-prettyprint (info)
"Pretty-printer for the git-fileinfo structure."
(insert (format " %s %s %s %s%s"
(if (git-fileinfo->marked info) (propertize "*" 'face 'git-mark-face) " ")
(git-status-code-as-string (git-fileinfo->state info))
(git-permissions-as-string (git-fileinfo->old-perm info) (git-fileinfo->new-perm info))
(git-escape-file-name (git-fileinfo->name info))
(insert (concat " " (if (git-fileinfo->marked info) (propertize "*" 'face 'git-mark-face) " ")
" " (git-status-code-as-string (git-fileinfo->state info))
" " (git-permissions-as-string (git-fileinfo->old-perm info) (git-fileinfo->new-perm info))
" " (git-escape-file-name (git-fileinfo->name info))
(git-rename-as-string info))))
(defun git-parse-status (status)

View File

@@ -355,7 +355,7 @@ then
# The name under $remote_top the remote HEAD seems to point at.
head_points_at=$(
(
echo "master"
test -f "$GIT_DIR/$remote_top/master" && echo "master"
cd "$GIT_DIR/$remote_top" &&
find . -type f -print | sed -e 's/^\.\///'
) | (

View File

@@ -107,6 +107,11 @@ extern int git_munmap(void *start, size_t length);
#define DEFAULT_PACKED_GIT_LIMIT \
((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256))
#ifdef NO_PREAD
#define pread git_pread
extern ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
#endif
#ifdef NO_SETENV
#define setenv gitsetenv
extern int gitsetenv(const char *, const char *, int);

1
git.c
View File

@@ -238,6 +238,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "ls-tree", cmd_ls_tree, RUN_SETUP },
{ "mailinfo", cmd_mailinfo },
{ "mailsplit", cmd_mailsplit },
{ "merge-base", cmd_merge_base, RUN_SETUP },
{ "merge-file", cmd_merge_file },
{ "mv", cmd_mv, RUN_SETUP },
{ "name-rev", cmd_name_rev, RUN_SETUP },

View File

@@ -2412,7 +2412,6 @@ sub git_patchset_body {
push @diff_header, $patch_line;
}
#last PATCH unless $patch_line;
my $last_patch_line = $patch_line;
# check if current patch belong to current raw line
@@ -2522,7 +2521,10 @@ sub git_patchset_body {
# from-file/to-file diff header
$patch_line = $last_patch_line;
last PATCH unless $patch_line;
if (! $patch_line) {
print "</div>\n"; # class="patch"
last PATCH;
}
next PATCH if ($patch_line =~ m/^diff /);
#assert($patch_line =~ m/^---/) if DEBUG;
if ($from{'href'} && $patch_line =~ m!^--- "?a/!) {
@@ -2533,7 +2535,6 @@ sub git_patchset_body {
print "<div class=\"diff from_file\">$patch_line</div>\n";
$patch_line = <$fd>;
#last PATCH unless $patch_line;
chomp $patch_line;
#assert($patch_line =~ m/^+++/) if DEBUG;

View File

@@ -809,6 +809,7 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
return error("Unable to start request");
}
target->pack_size = ftell(packfile);
fclose(packfile);
ret = move_temp_to_file(tmpfile, filename);

View File

@@ -770,11 +770,14 @@ static void finish_request(struct transfer_request *request)
request->url, curl_errorstr);
remote->can_update_info_refs = 0;
} else {
off_t pack_size = ftell(request->local_stream);
fclose(request->local_stream);
request->local_stream = NULL;
if (!move_temp_to_file(request->tmpfile,
request->filename)) {
target = (struct packed_git *)request->userData;
target->pack_size = pack_size;
lst = &remote->packs;
while (*lst != target)
lst = &((*lst)->next);

View File

@@ -199,10 +199,17 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
int retval;
void *tree;
struct tree_desc t;
unsigned char root[20];
tree = read_object_with_reference(tree_sha1, tree_type, &t.size, NULL);
tree = read_object_with_reference(tree_sha1, tree_type, &t.size, root);
if (!tree)
return -1;
if (name[0] == '\0') {
hashcpy(sha1, root);
return 0;
}
t.buf = tree;
retval = find_tree_entry(&t, name, sha1, mode);
free(tree);