From 5a17b54ad5543ddca60a493c613801279cc98a34 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 9 Jan 2007 02:52:31 -0800 Subject: [PATCH 01/15] Do not ignore a detected patchfile brokenness. find_header() function is used to read and parse the patchfile and it detects errors in the patch, but one place ignored the error and went ahead, which was quite bad. Noticed by Jeff Garzik. Signed-off-by: Junio C Hamano --- builtin-apply.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-apply.c b/builtin-apply.c index 61f047fd45..6a06be3025 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -812,7 +812,7 @@ 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); + return error("patch fragment without header at line %d: %.*s", linenr, (int)len-1, line); } if (size < len + 6) From 6534141151f7fd4334f62827d9234acf3974ca4d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 9 Jan 2007 11:50:53 -0800 Subject: [PATCH 02/15] Fix "Do not ignore a detected patchfile brokenness." Returning negative value from there does not stop the caller from using the earlier part. Noticed by Linus. Signed-off-by: Junio C Hamano --- builtin-apply.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin-apply.c b/builtin-apply.c index 6a06be3025..721d011bd0 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -812,7 +812,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; - return 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) From 2740b2b8539a322bc73f46c45c569503900c5dc1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 9 Jan 2007 14:07:59 -0800 Subject: [PATCH 03/15] builtin-archive: do not free a tree held by the object layer. Found by running "git archive --format=tar HEAD" in Documentation/ directory. It's surprising that nobody has noticed this from the beginning... Signed-off-by: Junio C Hamano --- builtin-archive.c | 1 - 1 file changed, 1 deletion(-) diff --git a/builtin-archive.c b/builtin-archive.c index 391cf43911..32737d3162 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -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; From d93b7d1c30c9716953392ec6590686301de0cc25 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 9 Jan 2007 11:11:47 -0500 Subject: [PATCH 04/15] get_tree_entry: map blank requested entry to tree root This means that git show HEAD: will now return HEAD^{tree}, which is logically consistent with git show HEAD:Documentation Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- tree-walk.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tree-walk.c b/tree-walk.c index 22f4550b6c..70f899957e 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -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); From d234b21c6966ee1d2dc4b5dcd9dca73a102bc442 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 9 Jan 2007 21:25:46 +0100 Subject: [PATCH 05/15] git-apply: Remove directories that have become empty after deleting a file. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- builtin-apply.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/builtin-apply.c b/builtin-apply.c index 38a9fdd808..54fd2cb0c7 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2239,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) From 3fe71f3a6f4454e845d4c7aec3e4bddd9a872b15 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 9 Jan 2007 21:26:52 +0100 Subject: [PATCH 06/15] git-clone: Make sure the master branch exists before running cat on it. Otherwise we get an error like this on stderr: cat: [...]/.git/refs/remotes/origin/master: No such file or directory which makes it look like git-clone failed. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- git-clone.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-clone.sh b/git-clone.sh index 3d388de62a..cf761b2c69 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -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/^\.\///' ) | ( From 03d311eda2d8c2d23855b9d3e904c7648925ab56 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 9 Jan 2007 21:27:40 +0100 Subject: [PATCH 07/15] git.el: Define the propertize function if needed, for XEmacs compatibility. Also use `concat' instead of `format' in the pretty-printer since format doesn't preserve properties under XEmacs. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index ede3ab2bd8..d90ba816e0 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -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) From 0bdb28c9ccd85b1c606664154b6f6d39a4c315fd Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Wed, 10 Jan 2007 00:07:43 +0100 Subject: [PATCH 08/15] gitweb: Fix git_patchset_body not closing
Fix case when git_patchset_body didn't close
, for patchsets with last patch empty. This patch also removes some commented out code in git_patchset_body. Signed-off-by: Jakub Narebski Acked-by: Luben Tuikov Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 25e5079a89..88af2e6380 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -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 "
\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 "
$patch_line
\n"; $patch_line = <$fd>; - #last PATCH unless $patch_line; chomp $patch_line; #assert($patch_line =~ m/^+++/) if DEBUG; From 6900679c2f6d937a5a6ef616869c8887690ad19d Mon Sep 17 00:00:00 2001 From: "Stefan-W. Hahn" Date: Tue, 9 Jan 2007 22:04:12 +0100 Subject: [PATCH 09/15] Replacing the system call pread() with lseek()/xread()/lseek() sequence. Using cygwin with cygwin.dll before 1.5.22 the system call pread() is buggy. This patch introduces NO_PREAD. If NO_PREAD is set git uses a sequence of lseek()/xread()/lseek() to emulate pread. Signed-off-by: Stefan-W. Hahn Signed-off-by: Junio C Hamano --- Makefile | 7 +++++++ compat/pread.c | 18 ++++++++++++++++++ git-compat-util.h | 5 +++++ 3 files changed, 30 insertions(+) create mode 100644 compat/pread.c diff --git a/Makefile b/Makefile index 6c12bc64ac..43113e9e16 100644 --- a/Makefile +++ b/Makefile @@ -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. # @@ -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 diff --git a/compat/pread.c b/compat/pread.c new file mode 100644 index 0000000000..978cac4ec9 --- /dev/null +++ b/compat/pread.c @@ -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; +} diff --git a/git-compat-util.h b/git-compat-util.h index e023bf1413..f8d46d587b 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -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); From cec21ca7cfd5d8fd19ea87e15f14dce6ee3b3859 Mon Sep 17 00:00:00 2001 From: Steven Grimm Date: Tue, 9 Jan 2007 16:20:17 -0800 Subject: [PATCH 10/15] Update git-svn manpage to remove the implication that SVN::* is optional. Now that git-svn requires the SVN::* Perl library, the manpage doesn't need to describe what happens when you don't have it. Signed-off-by: Steven Grimm Acked-by: Eric Wong Signed-off-by: Junio C Hamano --- Documentation/git-svn.txt | 65 ++++----------------------------------- 1 file changed, 6 insertions(+), 59 deletions(-) diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 8df43cb819..1b013139af 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -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[=]:: --repack-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 From 1c23d794bfa3b9f3e03b18bb7e542615a924dbe3 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 9 Jan 2007 20:04:52 -0500 Subject: [PATCH 11/15] Don't die in git-http-fetch when fetching packs. My sp/mmap changes to pack-check.c modified the function such that it expects packed_git.pack_size to be populated with the total bytecount of the packfile by the caller. But that isn't the case for packs obtained by git-http-fetch as pack_size was not initialized before being accessed. This caused verify_pack to think it had 2^32-21 bytes available when the downloaded pack perhaps was only 305 bytes in length. The use_pack function then later dies with "offset beyond end of packfile" when computing the overall file checksum. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- http-fetch.c | 1 + http-push.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/http-fetch.c b/http-fetch.c index fe8cd7bdcd..67dfb0a033 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -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); diff --git a/http-push.c b/http-push.c index 7e73eac9c3..0a15f53782 100644 --- a/http-push.c +++ b/http-push.c @@ -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); From 71dfbf224ff980f4085f75868dc409118418731e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 9 Jan 2007 00:50:02 -0800 Subject: [PATCH 12/15] Make merge-base a built-in. Signed-off-by: Junio C Hamano --- Makefile | 2 +- merge-base.c => builtin-merge-base.c | 12 +++++------- builtin.h | 1 + git.c | 1 + 4 files changed, 8 insertions(+), 8 deletions(-) rename merge-base.c => builtin-merge-base.c (79%) diff --git a/Makefile b/Makefile index 43113e9e16..eb88860bc9 100644 --- a/Makefile +++ b/Makefile @@ -194,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 \ @@ -289,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 \ diff --git a/merge-base.c b/builtin-merge-base.c similarity index 79% rename from merge-base.c rename to builtin-merge-base.c index 385f4ba386..e35d362f26 100644 --- a/merge-base.c +++ b/builtin-merge-base.c @@ -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] "; -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); } diff --git a/builtin.h b/builtin.h index df72d09447..ae32993ce9 100644 --- a/builtin.h +++ b/builtin.h @@ -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); diff --git a/git.c b/git.c index bf55499dc3..e7bc79af93 100644 --- a/git.c +++ b/git.c @@ -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 }, From 03840fc32d783be6750bf7e41a89687b8c3053eb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 8 Jan 2007 23:22:31 -0800 Subject: [PATCH 13/15] Allow in_merge_bases() to take more than one reference commits. The internal function in_merge_bases(A, B) is used to make sure that commit A is an ancestor of commit B. This changes the signature of it to take an array of B's and updates its current callers. Signed-off-by: Junio C Hamano --- builtin-branch.c | 2 +- builtin-reflog.c | 4 ++-- commit.c | 9 ++++++--- commit.h | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index d3df5a57f1..020ed6be7b 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -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, " diff --git a/builtin-reflog.c b/builtin-reflog.c index a967117661..fb37984ae6 100644 --- a/builtin-reflog.c +++ b/builtin-reflog.c @@ -217,8 +217,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) diff --git a/commit.c b/commit.c index 496d37aa02..aa14c5adb0 100644 --- a/commit.c +++ b/commit.c @@ -1158,14 +1158,17 @@ 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; - bases = get_merge_bases(rev1, rev2, 1); + if (num == 1) + bases = get_merge_bases(commit, *reference, 1); + else + die("not yet"); for (b = bases; b; b = b->next) { - if (!hashcmp(rev1->object.sha1, b->item->object.sha1)) { + if (!hashcmp(commit->object.sha1, b->item->object.sha1)) { ret = 1; break; } diff --git a/commit.h b/commit.h index 936f8fce30..b8e6e18a80 100644 --- a/commit.h +++ b/commit.h @@ -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 */ From 40e0e66bb00646fbdc998c4fe01280470489d0e5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 8 Jan 2007 23:26:19 -0800 Subject: [PATCH 14/15] merge_base(): move traversal into a separate function. Signed-off-by: Junio C Hamano --- commit.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/commit.c b/commit.c index aa14c5adb0..53f43e3638 100644 --- a/commit.c +++ b/commit.c @@ -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_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; @@ -1098,6 +1083,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); +} + struct commit_list *get_merge_bases(struct commit *one, struct commit *two, int cleanup) From dc3806d39698899fe81e55c0daa5bd9d30d13d9c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 9 Jan 2007 01:32:25 -0800 Subject: [PATCH 15/15] in_merge_bases(): optimization The callers of in_merge_bases() are interested in finding out if the given commit is reachable from others, and we do not have to compute the true merge base. Signed-off-by: Junio C Hamano --- commit.c | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/commit.c b/commit.c index 53f43e3638..bbfe7dc686 100644 --- a/commit.c +++ b/commit.c @@ -1034,7 +1034,7 @@ static struct commit *interesting(struct commit_list *list) return NULL; } -static struct commit_list *base_traverse(struct commit_list *list) +static struct commit_list *base_traverse(struct commit_list *list, struct commit *stop) { struct commit_list *result = NULL; @@ -1068,10 +1068,20 @@ static struct commit_list *base_traverse(struct commit_list *list) 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; @@ -1101,7 +1111,7 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two) insert_by_date(one, &list); insert_by_date(two, &list); - return base_traverse(list); + return base_traverse(list, NULL); } struct commit_list *get_merge_bases(struct commit *one, @@ -1166,20 +1176,30 @@ struct commit_list *get_merge_bases(struct commit *one, 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; - if (num == 1) - bases = get_merge_bases(commit, *reference, 1); - else - die("not yet"); - for (b = bases; b; b = b->next) { - if (!hashcmp(commit->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; }