From e14421b9aa85f11853a0dacae09498515daab7b8 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 29 Jun 2006 22:11:25 +0200 Subject: Allow INSTALL, bindir, mandir to be set in main Makefile Makefiles in subdirectories now use existing value of INSTALL, bindir, mandir if it is set, allowing those to be set in main Makefile or in included config.mak. Main Makefile exports variables which it sets. Accidentally it renames bin to bindir in Documentation/Makefile (should be bindir from start, but is unused, perhaps to be removed). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- Makefile | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Makefile') diff --git a/Makefile b/Makefile index cde619c498..b8fe669720 100644 --- a/Makefile +++ b/Makefile @@ -100,6 +100,8 @@ template_dir = $(prefix)/share/git-core/templates/ GIT_PYTHON_DIR = $(prefix)/share/git-core/python # DESTDIR= +export prefix bindir gitexecdir template_dir GIT_PYTHON_DIR + CC = gcc AR = ar TAR = tar -- cgit v1.3 From 7b8cf0cf2973cc8df3bdd36b9b36542b1f04d70a Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 29 Jun 2006 23:26:54 +0200 Subject: Rename man1 and man7 variables to man1dir and man7dir This patch renames man1 and man7 variables to man1dir and man7dir, according to "Makefile Conventions: Variables for Installation Directories" in make.info of GNU Make. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- Documentation/Makefile | 10 +++++----- Makefile | 4 ++-- contrib/git-svn/Makefile | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'Makefile') diff --git a/Documentation/Makefile b/Documentation/Makefile index ca6b77df28..cc83610588 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -27,8 +27,8 @@ DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT)) prefix?=$(HOME) bindir?=$(prefix)/bin mandir?=$(prefix)/man -man1=$(mandir)/man1 -man7=$(mandir)/man7 +man1dir=$(mandir)/man1 +man7dir=$(mandir)/man7 # DESTDIR= INSTALL?=install @@ -52,9 +52,9 @@ man1: $(DOC_MAN1) man7: $(DOC_MAN7) install: man - $(INSTALL) -d -m755 $(DESTDIR)$(man1) $(DESTDIR)$(man7) - $(INSTALL) $(DOC_MAN1) $(DESTDIR)$(man1) - $(INSTALL) $(DOC_MAN7) $(DESTDIR)$(man7) + $(INSTALL) -d -m755 $(DESTDIR)$(man1dir) $(DESTDIR)$(man7dir) + $(INSTALL) $(DOC_MAN1) $(DESTDIR)$(man1dir) + $(INSTALL) $(DOC_MAN7) $(DESTDIR)$(man7dir) # diff --git a/Makefile b/Makefile index b8fe669720..ccd7c62e57 100644 --- a/Makefile +++ b/Makefile @@ -714,8 +714,8 @@ dist-doc: rm -fr .doc-tmp-dir mkdir .doc-tmp-dir .doc-tmp-dir/man1 .doc-tmp-dir/man7 $(MAKE) -C Documentation DESTDIR=./ \ - man1=../.doc-tmp-dir/man1 \ - man7=../.doc-tmp-dir/man7 \ + man1dir=../.doc-tmp-dir/man1 \ + man7dir=../.doc-tmp-dir/man7 \ install cd .doc-tmp-dir && $(TAR) cf ../$(manpages).tar . gzip -n -9 -f $(manpages).tar diff --git a/contrib/git-svn/Makefile b/contrib/git-svn/Makefile index 1a6585eeec..8cac68873b 100644 --- a/contrib/git-svn/Makefile +++ b/contrib/git-svn/Makefile @@ -3,7 +3,7 @@ all: git-svn prefix?=$(HOME) bindir?=$(prefix)/bin mandir?=$(prefix)/man -man1=$(mandir)/man1 +man1dir=$(mandir)/man1 INSTALL?=install doc_conf=../../Documentation/asciidoc.conf -include ../../config.mak @@ -17,7 +17,7 @@ install: all $(INSTALL) git-svn $(DESTDIR)$(bindir) install-doc: doc - $(INSTALL) git-svn.1 $(DESTDIR)$(man1) + $(INSTALL) git-svn.1 $(DESTDIR)$(man1dir) doc: git-svn.1 git-svn.1 : git-svn.xml -- cgit v1.3 From 556677144b55aad8457851a9019e86c3676bd422 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Mon, 3 Jul 2006 01:56:48 +0200 Subject: autoconf: Use autoconf to write installation directories to config.mak.autogen This is beginning of patch series introducing installation configuration using autoconf (and no other autotools) to git. The idea is to generate config.mak.autogen using ./configure (generated from configure.ac by running autoconf) from config.mak.in, so one can use autoconf as an _alternative_ to ordinary Makefile, and creating one's own config.mak. Local settings in config.mak override generated settings in config.mak.autogen This patch includes minimal configure.ac and config.mak.in, so one can set installation directories using autoconf generated ./configure script e.g. ./configure --prefix=/usr Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- .gitignore | 6 ++++++ INSTALL | 9 +++++++++ Makefile | 1 + config.mak.in | 18 ++++++++++++++++++ configure.ac | 14 ++++++++++++++ 5 files changed, 48 insertions(+) create mode 100644 config.mak.in create mode 100644 configure.ac (limited to 'Makefile') diff --git a/.gitignore b/.gitignore index 7b954d587e..e103777026 100644 --- a/.gitignore +++ b/.gitignore @@ -135,4 +135,10 @@ git-core.spec *.[ao] *.py[co] config.mak +autom4te.cache +config.log +config.status +config.mak.in +config.mak.autogen +configure git-blame diff --git a/INSTALL b/INSTALL index f8337e2a4d..28245b3e68 100644 --- a/INSTALL +++ b/INSTALL @@ -13,6 +13,15 @@ that uses $prefix, the built results have some paths encoded, which are derived from $prefix, so "make all; make prefix=/usr install" would not work. +Alternatively you can use autoconf generated ./configure script to +set up install paths (via config.mak.autogen), so you can write instead + + $ autoconf ;# as yourself if ./configure doesn't exist yet + $ ./configure --prefix=/usr ;# as yourself + $ make all doc ;# as yourself + # make install install-doc ;# as root + + Issues of note: - git normally installs a helper script wrapper called "git", which diff --git a/Makefile b/Makefile index ccd7c62e57..a37d400591 100644 --- a/Makefile +++ b/Makefile @@ -333,6 +333,7 @@ ifneq (,$(findstring arm,$(uname_M))) ARM_SHA1 = YesPlease endif +-include config.mak.autogen -include config.mak ifdef WITH_OWN_SUBPROCESS_PY diff --git a/config.mak.in b/config.mak.in new file mode 100644 index 0000000000..82c9781f0d --- /dev/null +++ b/config.mak.in @@ -0,0 +1,18 @@ +# git Makefile configuration, included in main Makefile +# @configure_input@ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +#gitexecdir = @libexecdir@/git-core/ +template_dir = @datadir@/git-core/templates/ +GIT_PYTHON_DIR = @datadir@/git-core/python + +mandir=@mandir@ + +srcdir = @srcdir@ +VPATH = @srcdir@ + +export exec_prefix mandir +export srcdir VPATH + diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000000..a0374d4d53 --- /dev/null +++ b/configure.ac @@ -0,0 +1,14 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.59) +AC_INIT([git], [1.4.1], [git@vger.kernel.org]) + +AC_CONFIG_SRCDIR([git.c]) + +config_file=config.mak.autogen +config_in=config.mak.in + +# Output files +AC_CONFIG_FILES(["${config_file}":"${config_in}"]) +AC_OUTPUT -- cgit v1.3 From 92b878ade12f6672ce7ff7d12ac74b69c8e35c59 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 8 Jul 2006 23:07:07 +0200 Subject: Teach make clean about configure and autoconf Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- Makefile | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Makefile') diff --git a/Makefile b/Makefile index a37d400591..cef65861c4 100644 --- a/Makefile +++ b/Makefile @@ -729,6 +729,8 @@ clean: $(LIB_FILE) $(XDIFF_LIB) rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags + rm -rf autom4te.cache + rm -f config.log config.mak.autogen configure config.status config.cache rm -rf $(GIT_TARNAME) .doc-tmp-dir rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz rm -f $(htmldocs).tar.gz $(manpages).tar.gz -- cgit v1.3 From 8eb38cad440d8d51d8737d546a067a88361f6c3d Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Mon, 24 Jul 2006 00:28:28 -0400 Subject: Disable linking with Fink or DarwinPorts. It may be desirable for the compiler to disable linking against Fink or DarwinPorts, especially if both are installed on the system and the user wants GIT to be linked specifically to only one of them. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- Makefile | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) (limited to 'Makefile') diff --git a/Makefile b/Makefile index 01fb9cfdbd..3e085dfa6f 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,18 @@ all: # tests. These tests take up a significant amount of the total test time # but are not needed unless you plan to talk to SVN repos. # +# Define NO_FINK if you are building on Darwin/Mac OS X, have Fink +# installed in /sw, but don't want GIT to link against any libraries +# installed there. If defined you may specify your own (or Fink's) +# include directories and library directories by defining CFLAGS +# and LDFLAGS appropriately. +# +# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X, +# have DarwinPorts installed in /opt/local, but don't want GIT to +# link against any libraries installed there. If defined you may +# specify your own (or DarwinPort's) include directories and +# library directories by defining CFLAGS and LDFLAGS appropriately. +# # Define PPC_SHA1 environment variable when running make to make use of # a bundled SHA1 routine optimized for PowerPC. # @@ -254,15 +266,17 @@ ifeq ($(uname_S),Darwin) NEEDS_SSL_WITH_CRYPTO = YesPlease NEEDS_LIBICONV = YesPlease NO_STRLCPY = YesPlease - ## fink - ifeq ($(shell test -d /sw/lib && echo y),y) - ALL_CFLAGS += -I/sw/include - ALL_LDFLAGS += -L/sw/lib + ifndef NO_FINK + ifeq ($(shell test -d /sw/lib && echo y),y) + ALL_CFLAGS += -I/sw/include + ALL_LDFLAGS += -L/sw/lib + endif endif - ## darwinports - ifeq ($(shell test -d /opt/local/lib && echo y),y) - ALL_CFLAGS += -I/opt/local/include - ALL_LDFLAGS += -L/opt/local/lib + ifndef NO_DARWIN_PORTS + ifeq ($(shell test -d /opt/local/lib && echo y),y) + ALL_CFLAGS += -I/opt/local/include + ALL_LDFLAGS += -L/opt/local/lib + endif endif endif ifeq ($(uname_S),SunOS) -- cgit v1.3 From 941ba215fb9fc9e08705e86ea93e0f6f74d0faaf Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 26 Jul 2006 16:32:51 +0200 Subject: git-instaweb: respect bindir from Makefile Not everybody installs git to /usr/bin/git. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Makefile') diff --git a/Makefile b/Makefile index 3e085dfa6f..691e40b7b2 100644 --- a/Makefile +++ b/Makefile @@ -575,7 +575,7 @@ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css -e '/@@GITWEB_CGI@@/d' \ -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \ -e '/@@GITWEB_CSS@@/d' \ - $@.sh > $@+ + $@.sh | sed "s|/usr/bin/git|$(bindir)/git|" > $@+ chmod +x $@+ mv $@+ $@ -- cgit v1.3 From 11be42a47632a6f7219d34f5e312aa20ae076142 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 26 Jul 2006 03:52:35 +0200 Subject: Make git-mv a builtin This also moves add_file_to_index() to read-cache.c. Oh, and while touching builtin-add.c, it also removes a duplicate git_config() call. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Makefile | 11 +-- builtin-add.c | 40 ---------- builtin-mv.c | 221 +++++++++++++++++++++++++++++++++++++++++++++++++++ builtin.h | 1 + cache.h | 1 + git-mv.perl | 250 ---------------------------------------------------------- git.c | 1 + read-cache.c | 39 +++++++++ 8 files changed, 269 insertions(+), 295 deletions(-) create mode 100644 builtin-mv.c delete mode 100755 git-mv.perl (limited to 'Makefile') diff --git a/Makefile b/Makefile index 49eaa10b3b..73733e9025 100644 --- a/Makefile +++ b/Makefile @@ -151,7 +151,7 @@ SCRIPT_PERL = \ git-archimport.perl git-cvsimport.perl git-relink.perl \ git-shortlog.perl git-rerere.perl \ git-annotate.perl git-cvsserver.perl \ - git-svnimport.perl git-mv.perl git-cvsexportcommit.perl \ + git-svnimport.perl git-cvsexportcommit.perl \ git-send-email.perl git-svn.perl SCRIPT_PYTHON = \ @@ -192,7 +192,7 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X git-update-ref$X \ git-read-tree$X git-commit-tree$X git-write-tree$X \ git-apply$X git-show-branch$X git-diff-files$X git-update-index$X \ git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X \ - git-fmt-merge-msg$X git-prune$X + git-fmt-merge-msg$X git-prune$X git-mv$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -221,7 +221,7 @@ LIB_H = \ blob.h cache.h commit.h csum-file.h delta.h \ diff.h object.h pack.h pkt-line.h quote.h refs.h \ run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \ - tree-walk.h log-tree.h dir.h + tree-walk.h log-tree.h dir.h path-list.h DIFF_OBJS = \ diff.o diff-lib.o diffcore-break.o diffcore-order.o \ @@ -236,7 +236,7 @@ LIB_OBJS = \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \ - alloc.o merge-file.o $(DIFF_OBJS) + alloc.o merge-file.o path-list.o $(DIFF_OBJS) BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ @@ -248,7 +248,8 @@ BUILTIN_OBJS = \ builtin-apply.o builtin-show-branch.o builtin-diff-files.o \ builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \ builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \ - builtin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o + builtin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o \ + builtin-mv.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/builtin-add.c b/builtin-add.c index 3a73a173f7..72d2853176 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -82,45 +82,6 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec) prune_directory(dir, pathspec, baselen); } -static int add_file_to_index(const char *path, int verbose) -{ - int size, namelen; - struct stat st; - struct cache_entry *ce; - - if (lstat(path, &st)) - die("%s: unable to stat (%s)", path, strerror(errno)); - - if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) - die("%s: can only add regular files or symbolic links", path); - - namelen = strlen(path); - size = cache_entry_size(namelen); - ce = xcalloc(1, size); - memcpy(ce->name, path, namelen); - ce->ce_flags = htons(namelen); - fill_stat_cache_info(ce, &st); - - ce->ce_mode = create_ce_mode(st.st_mode); - if (!trust_executable_bit) { - /* If there is an existing entry, pick the mode bits - * from it. - */ - int pos = cache_name_pos(path, namelen); - if (pos >= 0) - ce->ce_mode = active_cache[pos]->ce_mode; - } - - if (index_path(ce->sha1, path, &st, 1)) - die("unable to index file %s", path); - if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD)) - die("unable to add %s to index",path); - if (verbose) - printf("add '%s'\n", path); - cache_tree_invalidate_path(active_cache_tree, path); - return 0; -} - static struct lock_file lock_file; int cmd_add(int argc, const char **argv, char **envp) @@ -159,7 +120,6 @@ int cmd_add(int argc, const char **argv, char **envp) } die(builtin_add_usage); } - git_config(git_default_config); pathspec = get_pathspec(prefix, argv + i); fill_directory(&dir, pathspec); diff --git a/builtin-mv.c b/builtin-mv.c new file mode 100644 index 0000000000..593ff9e434 --- /dev/null +++ b/builtin-mv.c @@ -0,0 +1,221 @@ +/* + * "git mv" builtin command + * + * Copyright (C) 2006 Johannes Schindelin + */ +#include + +#include "cache.h" +#include "builtin.h" +#include "dir.h" +#include "cache-tree.h" +#include "path-list.h" + +static const char builtin_mv_usage[] = +"git-mv [-n] [-f] ( | [-k] ... )"; + +static const char **copy_pathspec(const char *prefix, const char **pathspec, + int count, int base_name) +{ + const char **result = xmalloc((count + 1) * sizeof(const char *)); + memcpy(result, pathspec, count * sizeof(const char *)); + result[count] = NULL; + if (base_name) { + int i; + for (i = 0; i < count; i++) { + const char *last_slash = strrchr(result[i], '/'); + if (last_slash) + result[i] = last_slash + 1; + } + } + return get_pathspec(prefix, result); +} + +static void show_list(const char *label, struct path_list *list) +{ + if (list->nr > 0) { + int i; + printf("%s", label); + for (i = 0; i < list->nr; i++) + printf("%s%s", i > 0 ? ", " : "", list->items[i].path); + putchar('\n'); + } +} + +static struct lock_file lock_file; + +int cmd_mv(int argc, const char **argv, char **envp) +{ + int i, newfd, count; + int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; + const char *prefix = setup_git_directory(); + const char **source, **destination, **dest_path; + struct stat st; + struct path_list overwritten = {NULL, 0, 0, 0}; + struct path_list src_for_dst = {NULL, 0, 0, 0}; + struct path_list added = {NULL, 0, 0, 0}; + struct path_list deleted = {NULL, 0, 0, 0}; + struct path_list changed = {NULL, 0, 0, 0}; + + git_config(git_default_config); + + newfd = hold_lock_file_for_update(&lock_file, get_index_file()); + if (newfd < 0) + die("unable to create new index file"); + + if (read_cache() < 0) + die("index file corrupt"); + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + if (arg[0] != '-') + break; + if (!strcmp(arg, "--")) { + i++; + break; + } + if (!strcmp(arg, "-n")) { + show_only = 1; + continue; + } + if (!strcmp(arg, "-f")) { + force = 1; + continue; + } + if (!strcmp(arg, "-k")) { + ignore_errors = 1; + continue; + } + die(builtin_mv_usage); + } + count = argc - i - 1; + if (count < 1) + usage(builtin_mv_usage); + + source = copy_pathspec(prefix, argv + i, count, 0); + dest_path = copy_pathspec(prefix, argv + argc - 1, 1, 0); + + if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) + destination = copy_pathspec(dest_path[0], argv + i, count, 1); + else { + if (count != 1) + usage(builtin_mv_usage); + destination = dest_path; + } + + /* Checking */ + for (i = 0; i < count; i++) { + const char *bad = NULL; + + if (show_only) + printf("Checking rename of '%s' to '%s'\n", + source[i], destination[i]); + + if (lstat(source[i], &st) < 0) + bad = "bad source"; + else if (lstat(destination[i], &st) == 0) { + bad = "destination exists"; + if (force) { + /* + * only files can overwrite each other: + * check both source and destination + */ + if (S_ISREG(st.st_mode)) { + fprintf(stderr, "Warning: %s;" + " will overwrite!\n", + bad); + bad = NULL; + path_list_insert(destination[i], + &overwritten); + } else + bad = "Cannot overwrite"; + } + } + + if (!bad && + !strncmp(destination[i], source[i], strlen(source[i]))) + bad = "can not move directory into itself"; + + if (!bad && cache_name_pos(source[i], strlen(source[i])) < 0) + bad = "not under version control"; + + if (!bad) { + if (path_list_has_path(&src_for_dst, destination[i])) + bad = "multiple sources for the same target"; + else + path_list_insert(destination[i], &src_for_dst); + } + + if (bad) { + if (ignore_errors) { + if (--count > 0) { + memmove(source + i, source + i + 1, + (count - i) * sizeof(char *)); + memmove(destination + i, + destination + i + 1, + (count - i) * sizeof(char *)); + } + } else + die ("Error: %s, source=%s, destination=%s", + bad, source[i], destination[i]); + } + } + + for (i = 0; i < count; i++) { + if (show_only || verbose) + printf("Renaming %s to %s\n", + source[i], destination[i]); + if (!show_only && + rename(source[i], destination[i]) < 0 && + !ignore_errors) + die ("renaming %s failed: %s", + source[i], strerror(errno)); + + if (cache_name_pos(source[i], strlen(source[i])) >= 0) { + path_list_insert(source[i], &deleted); + + /* destination can be a directory with 1 file inside */ + if (path_list_has_path(&overwritten, destination[i])) + path_list_insert(destination[i], &changed); + else + path_list_insert(destination[i], &added); + } else + path_list_insert(destination[i], &added); + } + + if (show_only) { + show_list("Changed : ", &changed); + show_list("Adding : ", &added); + show_list("Deleting : ", &deleted); + } else { + for (i = 0; i < changed.nr; i++) { + const char *path = changed.items[i].path; + int i = cache_name_pos(path, strlen(path)); + struct cache_entry *ce = active_cache[i]; + + if (i < 0) + die ("Huh? Cache entry for %s unknown?", path); + refresh_cache_entry(ce, 0); + } + + for (i = 0; i < added.nr; i++) { + const char *path = added.items[i].path; + add_file_to_index(path, verbose); + } + + for (i = 0; i < deleted.nr; i++) { + const char *path = deleted.items[i].path; + remove_file_from_cache(path); + } + + if (active_cache_changed) { + if (write_cache(newfd, active_cache, active_nr) || + close(newfd) || + commit_lock_file(&lock_file)) + die("Unable to write new index file"); + } + } + + return 0; +} diff --git a/builtin.h b/builtin.h index 5339d8627f..6f3a43957c 100644 --- a/builtin.h +++ b/builtin.h @@ -52,6 +52,7 @@ extern int cmd_rev_parse(int argc, const char **argv, char **envp); extern int cmd_update_index(int argc, const char **argv, char **envp); extern int cmd_update_ref(int argc, const char **argv, char **envp); extern int cmd_fmt_merge_msg(int argc, const char **argv, char **envp); +extern int cmd_mv(int argc, const char **argv, char **envp); extern int cmd_write_tree(int argc, const char **argv, char **envp); extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix); diff --git a/cache.h b/cache.h index d0b1f2730f..c575b8a996 100644 --- a/cache.h +++ b/cache.h @@ -154,6 +154,7 @@ extern int add_cache_entry(struct cache_entry *ce, int option); extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really); extern int remove_cache_entry_at(int pos); extern int remove_file_from_cache(const char *path); +extern int add_file_to_index(const char *path, int verbose); extern int ce_same_name(struct cache_entry *a, struct cache_entry *b); extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int); extern int ce_modified(struct cache_entry *ce, struct stat *st, int); diff --git a/git-mv.perl b/git-mv.perl deleted file mode 100755 index 75aa8feeb6..0000000000 --- a/git-mv.perl +++ /dev/null @@ -1,250 +0,0 @@ -#!/usr/bin/perl -# -# Copyright 2005, Ryan Anderson -# Josef Weidendorfer -# -# This file is licensed under the GPL v2, or a later version -# at the discretion of Linus Torvalds. - - -use warnings; -use strict; -use Getopt::Std; - -sub usage() { - print < -$0 [-f] [-n] [-k] ... -EOT - exit(1); -} - -our ($opt_n, $opt_f, $opt_h, $opt_k, $opt_v); -getopts("hnfkv") || usage; -usage() if $opt_h; -@ARGV >= 1 or usage; - -my $GIT_DIR = `git rev-parse --git-dir`; -exit 1 if $?; # rev-parse would have given "not a git dir" message. -chomp($GIT_DIR); - -my (@srcArgs, @dstArgs, @srcs, @dsts); -my ($src, $dst, $base, $dstDir); - -# remove any trailing slash in arguments -for (@ARGV) { s/\/*$//; } - -my $argCount = scalar @ARGV; -if (-d $ARGV[$argCount-1]) { - $dstDir = $ARGV[$argCount-1]; - @srcArgs = @ARGV[0..$argCount-2]; - - foreach $src (@srcArgs) { - $base = $src; - $base =~ s/^.*\///; - $dst = "$dstDir/". $base; - push @dstArgs, $dst; - } -} -else { - if ($argCount < 2) { - print "Error: need at least two arguments\n"; - exit(1); - } - if ($argCount > 2) { - print "Error: moving to directory '" - . $ARGV[$argCount-1] - . "' not possible; not existing\n"; - exit(1); - } - @srcArgs = ($ARGV[0]); - @dstArgs = ($ARGV[1]); - $dstDir = ""; -} - -my $subdir_prefix = `git rev-parse --show-prefix`; -chomp($subdir_prefix); - -# run in git base directory, so that git-ls-files lists all revisioned files -chdir "$GIT_DIR/.."; - -# normalize paths, needed to compare against versioned files and update-index -# also, this is nicer to end-users by doing ".//a/./b/.//./c" ==> "a/b/c" -for (@srcArgs, @dstArgs) { - # prepend git prefix as we run from base directory - $_ = $subdir_prefix.$_; - s|^\./||; - s|/\./|/| while (m|/\./|); - s|//+|/|g; - # Also "a/b/../c" ==> "a/c" - 1 while (s,(^|/)[^/]+/\.\./,$1,); -} - -my (@allfiles,@srcfiles,@dstfiles); -my $safesrc; -my (%overwritten, %srcForDst); - -$/ = "\0"; -open(F, 'git-ls-files -z |') - or die "Failed to open pipe from git-ls-files: " . $!; - -@allfiles = map { chomp; $_; } ; -close(F); - - -my ($i, $bad); -while(scalar @srcArgs > 0) { - $src = shift @srcArgs; - $dst = shift @dstArgs; - $bad = ""; - - for ($src, $dst) { - # Be nicer to end-users by doing ".//a/./b/.//./c" ==> "a/b/c" - s|^\./||; - s|/\./|/| while (m|/\./|); - s|//+|/|g; - # Also "a/b/../c" ==> "a/c" - 1 while (s,(^|/)[^/]+/\.\./,$1,); - } - - if ($opt_v) { - print "Checking rename of '$src' to '$dst'\n"; - } - - unless (-f $src || -l $src || -d $src) { - $bad = "bad source '$src'"; - } - - $safesrc = quotemeta($src); - @srcfiles = grep /^$safesrc(\/|$)/, @allfiles; - - $overwritten{$dst} = 0; - if (($bad eq "") && -e $dst) { - $bad = "destination '$dst' already exists"; - if ($opt_f) { - # only files can overwrite each other: check both source and destination - if (-f $dst && (scalar @srcfiles == 1)) { - print "Warning: $bad; will overwrite!\n"; - $bad = ""; - $overwritten{$dst} = 1; - } - else { - $bad = "Can not overwrite '$src' with '$dst'"; - } - } - } - - if (($bad eq "") && ($dst =~ /^$safesrc\//)) { - $bad = "can not move directory '$src' into itself"; - } - - if ($bad eq "") { - if (scalar @srcfiles == 0) { - $bad = "'$src' not under version control"; - } - } - - if ($bad eq "") { - if (defined $srcForDst{$dst}) { - $bad = "can not move '$src' to '$dst'; already target of "; - $bad .= "'".$srcForDst{$dst}."'"; - } - else { - $srcForDst{$dst} = $src; - } - } - - if ($bad ne "") { - if ($opt_k) { - print "Warning: $bad; skipping\n"; - next; - } - print "Error: $bad\n"; - exit(1); - } - push @srcs, $src; - push @dsts, $dst; -} - -# Final pass: rename/move -my (@deletedfiles,@addedfiles,@changedfiles); -$bad = ""; -while(scalar @srcs > 0) { - $src = shift @srcs; - $dst = shift @dsts; - - if ($opt_n || $opt_v) { print "Renaming $src to $dst\n"; } - if (!$opt_n) { - if (!rename($src,$dst)) { - $bad = "renaming '$src' failed: $!"; - if ($opt_k) { - print "Warning: skipped: $bad\n"; - $bad = ""; - next; - } - last; - } - } - - $safesrc = quotemeta($src); - @srcfiles = grep /^$safesrc(\/|$)/, @allfiles; - @dstfiles = @srcfiles; - s/^$safesrc(\/|$)/$dst$1/ for @dstfiles; - - push @deletedfiles, @srcfiles; - if (scalar @srcfiles == 1) { - # $dst can be a directory with 1 file inside - if ($overwritten{$dst} ==1) { - push @changedfiles, $dstfiles[0]; - - } else { - push @addedfiles, $dstfiles[0]; - } - } - else { - push @addedfiles, @dstfiles; - } -} - -if ($opt_n) { - if (@changedfiles) { - print "Changed : ". join(", ", @changedfiles) ."\n"; - } - if (@addedfiles) { - print "Adding : ". join(", ", @addedfiles) ."\n"; - } - if (@deletedfiles) { - print "Deleting : ". join(", ", @deletedfiles) ."\n"; - } -} -else { - if (@changedfiles) { - open(H, "| git-update-index -z --stdin") - or die "git-update-index failed to update changed files with code $!\n"; - foreach my $fileName (@changedfiles) { - print H "$fileName\0"; - } - close(H); - } - if (@addedfiles) { - open(H, "| git-update-index --add -z --stdin") - or die "git-update-index failed to add new names with code $!\n"; - foreach my $fileName (@addedfiles) { - print H "$fileName\0"; - } - close(H); - } - if (@deletedfiles) { - open(H, "| git-update-index --remove -z --stdin") - or die "git-update-index failed to remove old names with code $!\n"; - foreach my $fileName (@deletedfiles) { - print H "$fileName\0"; - } - close(H); - } -} - -if ($bad ne "") { - print "Error: $bad\n"; - exit(1); -} diff --git a/git.c b/git.c index ee5a0e86a7..d47e9d8db9 100644 --- a/git.c +++ b/git.c @@ -202,6 +202,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "update-ref", cmd_update_ref }, { "fmt-merge-msg", cmd_fmt_merge_msg }, { "prune", cmd_prune }, + { "mv", cmd_mv }, }; int i; diff --git a/read-cache.c b/read-cache.c index 4c47a0eb9f..c0b031367b 100644 --- a/read-cache.c +++ b/read-cache.c @@ -319,6 +319,45 @@ int remove_file_from_cache(const char *path) return 0; } +int add_file_to_index(const char *path, int verbose) +{ + int size, namelen; + struct stat st; + struct cache_entry *ce; + + if (lstat(path, &st)) + die("%s: unable to stat (%s)", path, strerror(errno)); + + if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) + die("%s: can only add regular files or symbolic links", path); + + namelen = strlen(path); + size = cache_entry_size(namelen); + ce = xcalloc(1, size); + memcpy(ce->name, path, namelen); + ce->ce_flags = htons(namelen); + fill_stat_cache_info(ce, &st); + + ce->ce_mode = create_ce_mode(st.st_mode); + if (!trust_executable_bit) { + /* If there is an existing entry, pick the mode bits + * from it. + */ + int pos = cache_name_pos(path, namelen); + if (pos >= 0) + ce->ce_mode = active_cache[pos]->ce_mode; + } + + if (index_path(ce->sha1, path, &st, 1)) + die("unable to index file %s", path); + if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD)) + die("unable to add %s to index",path); + if (verbose) + printf("add '%s'\n", path); + cache_tree_invalidate_path(active_cache_tree, path); + return 0; +} + int ce_same_name(struct cache_entry *a, struct cache_entry *b) { int len = ce_namelen(a); -- cgit v1.3 From 2941cab99ca9dec3356f0126be41bc3995bb1fef Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 28 Jul 2006 11:17:46 +0200 Subject: Makefile: ssh-pull.o depends on ssh-fetch.c Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Makefile | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Makefile') diff --git a/Makefile b/Makefile index 49eaa10b3b..8349e3defa 100644 --- a/Makefile +++ b/Makefile @@ -614,6 +614,8 @@ $(SIMPLE_PROGRAMS) : git-%$X : %.o $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIB_FILE) $(SIMPLE_LIB) +ssh-pull.o: ssh-fetch.c +ssh-push.o: ssh-upload.c git-local-fetch$X: fetch.o git-ssh-fetch$X: rsh.o fetch.o git-ssh-upload$X: rsh.o -- cgit v1.3 From ca7a741647f961eb3b6b9df14d079c99fdc9a827 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Sat, 29 Jul 2006 16:26:18 +0000 Subject: Build on Debian GNU/kFreeBSD Patch from Petr Salinger to make the build process detect and support the Debian GNU/kFreeBSD architecture, see http://bugs.debian.org/380209 Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- Makefile | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Makefile') diff --git a/Makefile b/Makefile index e9dce268f8..15864e23bd 100644 --- a/Makefile +++ b/Makefile @@ -265,6 +265,9 @@ LIBS = $(GITLIBS) -lz ifeq ($(uname_S),Linux) NO_STRLCPY = YesPlease endif +ifeq ($(uname_S),GNU/kFreeBSD) + NO_STRLCPY = YesPlease +endif ifeq ($(uname_S),Darwin) NEEDS_SSL_WITH_CRYPTO = YesPlease NEEDS_LIBICONV = YesPlease -- cgit v1.3 From 16da134b1f9b0fbd1d328999875d34d02e5470b4 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 30 Jul 2006 20:25:18 +0200 Subject: read-trees: refactor the unpack_trees() part Basically, the options are passed by a struct unpack_trees_options now. That's all. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Makefile | 4 +- builtin-read-tree.c | 577 +++++++++------------------------------------------- unpack-trees.c | 395 +++++++++++++++++++++++++++++++++++ unpack-trees.h | 30 +++ 4 files changed, 521 insertions(+), 485 deletions(-) create mode 100644 unpack-trees.c create mode 100644 unpack-trees.h (limited to 'Makefile') diff --git a/Makefile b/Makefile index 15864e23bd..b2332761ba 100644 --- a/Makefile +++ b/Makefile @@ -221,7 +221,7 @@ LIB_H = \ blob.h cache.h commit.h csum-file.h delta.h \ diff.h object.h pack.h pkt-line.h quote.h refs.h \ run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \ - tree-walk.h log-tree.h dir.h path-list.h + tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h DIFF_OBJS = \ diff.o diff-lib.o diffcore-break.o diffcore-order.o \ @@ -236,7 +236,7 @@ LIB_OBJS = \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \ - alloc.o merge-file.o path-list.o $(DIFF_OBJS) + alloc.o merge-file.o path-list.o unpack-trees.o $(DIFF_OBJS) BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 49c10bf221..6bc042f852 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -11,411 +11,17 @@ #include "tree.h" #include "tree-walk.h" #include "cache-tree.h" -#include -#include +#include "unpack-trees.h" #include "builtin.h" -static int reset = 0; -static int merge = 0; -static int update = 0; -static int index_only = 0; -static int nontrivial_merge = 0; -static int trivial_merges_only = 0; -static int aggressive = 0; -static int verbose_update = 0; -static volatile int progress_update = 0; -static const char *prefix = NULL; - -static int head_idx = -1; -static int merge_size = 0; - static struct object_list *trees = NULL; -static struct cache_entry df_conflict_entry; - -struct tree_entry_list { - struct tree_entry_list *next; - unsigned directory : 1; - unsigned executable : 1; - unsigned symlink : 1; - unsigned int mode; - const char *name; - const unsigned char *sha1; -}; - -static struct tree_entry_list df_conflict_list; - -typedef int (*merge_fn_t)(struct cache_entry **src); - -static struct tree_entry_list *create_tree_entry_list(struct tree *tree) -{ - struct tree_desc desc; - struct name_entry one; - struct tree_entry_list *ret = NULL; - struct tree_entry_list **list_p = &ret; - - desc.buf = tree->buffer; - desc.size = tree->size; - - while (tree_entry(&desc, &one)) { - struct tree_entry_list *entry; - - entry = xmalloc(sizeof(struct tree_entry_list)); - entry->name = one.path; - entry->sha1 = one.sha1; - entry->mode = one.mode; - entry->directory = S_ISDIR(one.mode) != 0; - entry->executable = (one.mode & S_IXUSR) != 0; - entry->symlink = S_ISLNK(one.mode) != 0; - entry->next = NULL; - - *list_p = entry; - list_p = &entry->next; - } - return ret; -} - -static int entcmp(const char *name1, int dir1, const char *name2, int dir2) -{ - int len1 = strlen(name1); - int len2 = strlen(name2); - int len = len1 < len2 ? len1 : len2; - int ret = memcmp(name1, name2, len); - unsigned char c1, c2; - if (ret) - return ret; - c1 = name1[len]; - c2 = name2[len]; - if (!c1 && dir1) - c1 = '/'; - if (!c2 && dir2) - c2 = '/'; - ret = (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0; - if (c1 && c2 && !ret) - ret = len1 - len2; - return ret; -} - -static int unpack_trees_rec(struct tree_entry_list **posns, int len, - const char *base, merge_fn_t fn, int *indpos) -{ - int baselen = strlen(base); - int src_size = len + 1; - do { - int i; - const char *first; - int firstdir = 0; - int pathlen; - unsigned ce_size; - struct tree_entry_list **subposns; - struct cache_entry **src; - int any_files = 0; - int any_dirs = 0; - char *cache_name; - int ce_stage; - - /* Find the first name in the input. */ - - first = NULL; - cache_name = NULL; - - /* Check the cache */ - if (merge && *indpos < active_nr) { - /* This is a bit tricky: */ - /* If the index has a subdirectory (with - * contents) as the first name, it'll get a - * filename like "foo/bar". But that's after - * "foo", so the entry in trees will get - * handled first, at which point we'll go into - * "foo", and deal with "bar" from the index, - * because the base will be "foo/". The only - * way we can actually have "foo/bar" first of - * all the things is if the trees don't - * contain "foo" at all, in which case we'll - * handle "foo/bar" without going into the - * directory, but that's fine (and will return - * an error anyway, with the added unknown - * file case. - */ - - cache_name = active_cache[*indpos]->name; - if (strlen(cache_name) > baselen && - !memcmp(cache_name, base, baselen)) { - cache_name += baselen; - first = cache_name; - } else { - cache_name = NULL; - } - } - -#if DBRT_DEBUG > 1 - if (first) - printf("index %s\n", first); -#endif - for (i = 0; i < len; i++) { - if (!posns[i] || posns[i] == &df_conflict_list) - continue; -#if DBRT_DEBUG > 1 - printf("%d %s\n", i + 1, posns[i]->name); -#endif - if (!first || entcmp(first, firstdir, - posns[i]->name, - posns[i]->directory) > 0) { - first = posns[i]->name; - firstdir = posns[i]->directory; - } - } - /* No name means we're done */ - if (!first) - return 0; - - pathlen = strlen(first); - ce_size = cache_entry_size(baselen + pathlen); - - src = xcalloc(src_size, sizeof(struct cache_entry *)); - - subposns = xcalloc(len, sizeof(struct tree_list_entry *)); - - if (cache_name && !strcmp(cache_name, first)) { - any_files = 1; - src[0] = active_cache[*indpos]; - remove_cache_entry_at(*indpos); - } - - for (i = 0; i < len; i++) { - struct cache_entry *ce; - - if (!posns[i] || - (posns[i] != &df_conflict_list && - strcmp(first, posns[i]->name))) { - continue; - } - - if (posns[i] == &df_conflict_list) { - src[i + merge] = &df_conflict_entry; - continue; - } - - if (posns[i]->directory) { - struct tree *tree = lookup_tree(posns[i]->sha1); - any_dirs = 1; - parse_tree(tree); - subposns[i] = create_tree_entry_list(tree); - posns[i] = posns[i]->next; - src[i + merge] = &df_conflict_entry; - continue; - } - - if (!merge) - ce_stage = 0; - else if (i + 1 < head_idx) - ce_stage = 1; - else if (i + 1 > head_idx) - ce_stage = 3; - else - ce_stage = 2; - - ce = xcalloc(1, ce_size); - ce->ce_mode = create_ce_mode(posns[i]->mode); - ce->ce_flags = create_ce_flags(baselen + pathlen, - ce_stage); - memcpy(ce->name, base, baselen); - memcpy(ce->name + baselen, first, pathlen + 1); - - any_files = 1; - - memcpy(ce->sha1, posns[i]->sha1, 20); - src[i + merge] = ce; - subposns[i] = &df_conflict_list; - posns[i] = posns[i]->next; - } - if (any_files) { - if (merge) { - int ret; - -#if DBRT_DEBUG > 1 - printf("%s:\n", first); - for (i = 0; i < src_size; i++) { - printf(" %d ", i); - if (src[i]) - printf("%s\n", sha1_to_hex(src[i]->sha1)); - else - printf("\n"); - } -#endif - ret = fn(src); - -#if DBRT_DEBUG > 1 - printf("Added %d entries\n", ret); -#endif - *indpos += ret; - } else { - for (i = 0; i < src_size; i++) { - if (src[i]) { - add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK); - } - } - } - } - if (any_dirs) { - char *newbase = xmalloc(baselen + 2 + pathlen); - memcpy(newbase, base, baselen); - memcpy(newbase + baselen, first, pathlen); - newbase[baselen + pathlen] = '/'; - newbase[baselen + pathlen + 1] = '\0'; - if (unpack_trees_rec(subposns, len, newbase, fn, - indpos)) - return -1; - free(newbase); - } - free(subposns); - free(src); - } while (1); -} - static void reject_merge(struct cache_entry *ce) { - die("Entry '%s' would be overwritten by merge. Cannot merge.", + die("Entry '%s' would be overwritten by merge. Cannot merge.", ce->name); } -/* Unlink the last component and attempt to remove leading - * directories, in case this unlink is the removal of the - * last entry in the directory -- empty directories are removed. - */ -static void unlink_entry(char *name) -{ - char *cp, *prev; - - if (unlink(name)) - return; - prev = NULL; - while (1) { - int status; - cp = strrchr(name, '/'); - if (prev) - *prev = '/'; - if (!cp) - break; - - *cp = 0; - status = rmdir(name); - if (status) { - *cp = '/'; - break; - } - prev = cp; - } -} - -static void progress_interval(int signum) -{ - progress_update = 1; -} - -static void setup_progress_signal(void) -{ - struct sigaction sa; - struct itimerval v; - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = progress_interval; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - sigaction(SIGALRM, &sa, NULL); - - v.it_interval.tv_sec = 1; - v.it_interval.tv_usec = 0; - v.it_value = v.it_interval; - setitimer(ITIMER_REAL, &v, NULL); -} - -static struct checkout state; -static void check_updates(struct cache_entry **src, int nr) -{ - unsigned short mask = htons(CE_UPDATE); - unsigned last_percent = 200, cnt = 0, total = 0; - - if (update && verbose_update) { - for (total = cnt = 0; cnt < nr; cnt++) { - struct cache_entry *ce = src[cnt]; - if (!ce->ce_mode || ce->ce_flags & mask) - total++; - } - - /* Don't bother doing this for very small updates */ - if (total < 250) - total = 0; - - if (total) { - fprintf(stderr, "Checking files out...\n"); - setup_progress_signal(); - progress_update = 1; - } - cnt = 0; - } - - while (nr--) { - struct cache_entry *ce = *src++; - - if (total) { - if (!ce->ce_mode || ce->ce_flags & mask) { - unsigned percent; - cnt++; - percent = (cnt * 100) / total; - if (percent != last_percent || - progress_update) { - fprintf(stderr, "%4u%% (%u/%u) done\r", - percent, cnt, total); - last_percent = percent; - progress_update = 0; - } - } - } - if (!ce->ce_mode) { - if (update) - unlink_entry(ce->name); - continue; - } - if (ce->ce_flags & mask) { - ce->ce_flags &= ~mask; - if (update) - checkout_entry(ce, &state, NULL); - } - } - if (total) { - signal(SIGALRM, SIG_IGN); - fputc('\n', stderr); - } -} - -static int unpack_trees(merge_fn_t fn) -{ - int indpos = 0; - unsigned len = object_list_length(trees); - struct tree_entry_list **posns; - int i; - struct object_list *posn = trees; - merge_size = len; - - if (len) { - posns = xmalloc(len * sizeof(struct tree_entry_list *)); - for (i = 0; i < len; i++) { - posns[i] = create_tree_entry_list((struct tree *) posn->item); - posn = posn->next; - } - if (unpack_trees_rec(posns, len, prefix ? prefix : "", - fn, &indpos)) - return -1; - } - - if (trivial_merges_only && nontrivial_merge) - die("Merge requires file-level merging"); - - check_updates(active_cache, active_nr); - return 0; -} - static int list_tree(unsigned char *sha1) { struct tree *tree = parse_tree_indirect(sha1); @@ -440,11 +46,12 @@ static int same(struct cache_entry *a, struct cache_entry *b) * When a CE gets turned into an unmerged entry, we * want it to be up-to-date */ -static void verify_uptodate(struct cache_entry *ce) +static void verify_uptodate(struct cache_entry *ce, + struct unpack_trees_options *o) { struct stat st; - if (index_only || reset) + if (o->index_only || o->reset) return; if (!lstat(ce->name, &st)) { @@ -453,7 +60,7 @@ static void verify_uptodate(struct cache_entry *ce) return; errno = 0; } - if (reset) { + if (o->reset) { ce->ce_flags |= htons(CE_UPDATE); return; } @@ -472,18 +79,20 @@ static void invalidate_ce_path(struct cache_entry *ce) * We do not want to remove or overwrite a working tree file that * is not tracked. */ -static void verify_absent(const char *path, const char *action) +static void verify_absent(const char *path, const char *action, + struct unpack_trees_options *o) { struct stat st; - if (index_only || reset || !update) + if (o->index_only || o->reset || !o->update) return; if (!lstat(path, &st)) die("Untracked working tree file '%s' " "would be %s by merge.", path, action); } -static int merged_entry(struct cache_entry *merge, struct cache_entry *old) +static int merged_entry(struct cache_entry *merge, struct cache_entry *old, + struct unpack_trees_options *o) { merge->ce_flags |= htons(CE_UPDATE); if (old) { @@ -497,12 +106,12 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old) if (same(old, merge)) { *merge = *old; } else { - verify_uptodate(old); + verify_uptodate(old, o); invalidate_ce_path(old); } } else { - verify_absent(merge->name, "overwritten"); + verify_absent(merge->name, "overwritten", o); invalidate_ce_path(merge); } @@ -511,12 +120,13 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old) return 1; } -static int deleted_entry(struct cache_entry *ce, struct cache_entry *old) +static int deleted_entry(struct cache_entry *ce, struct cache_entry *old, + struct unpack_trees_options *o) { if (old) - verify_uptodate(old); + verify_uptodate(old, o); else - verify_absent(ce->name, "removed"); + verify_absent(ce->name, "removed", o); ce->ce_mode = 0; add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); invalidate_ce_path(ce); @@ -545,11 +155,12 @@ static void show_stage_entry(FILE *o, } #endif -static int threeway_merge(struct cache_entry **stages) +static int threeway_merge(struct cache_entry **stages, + struct unpack_trees_options *o) { struct cache_entry *index; - struct cache_entry *head; - struct cache_entry *remote = stages[head_idx + 1]; + struct cache_entry *head; + struct cache_entry *remote = stages[o->head_idx + 1]; int count; int head_match = 0; int remote_match = 0; @@ -562,7 +173,7 @@ static int threeway_merge(struct cache_entry **stages) int no_anc_exists = 1; int i; - for (i = 1; i < head_idx; i++) { + for (i = 1; i < o->head_idx; i++) { if (!stages[i]) any_anc_missing = 1; else { @@ -573,14 +184,14 @@ static int threeway_merge(struct cache_entry **stages) } index = stages[0]; - head = stages[head_idx]; + head = stages[o->head_idx]; - if (head == &df_conflict_entry) { + if (head == &o->df_conflict_entry) { df_conflict_head = 1; head = NULL; } - if (remote == &df_conflict_entry) { + if (remote == &o->df_conflict_entry) { df_conflict_remote = 1; remote = NULL; } @@ -596,7 +207,7 @@ static int threeway_merge(struct cache_entry **stages) * and #14. */ if (!same(remote, head)) { - for (i = 1; i < head_idx; i++) { + for (i = 1; i < o->head_idx; i++) { if (same(stages[i], head)) { head_match = i; } @@ -614,7 +225,7 @@ static int threeway_merge(struct cache_entry **stages) if (remote && !df_conflict_head && head_match && !remote_match) { if (index && !same(index, remote) && !same(index, head)) reject_merge(index); - return merged_entry(remote, index); + return merged_entry(remote, index, o); } /* * If we have an entry in the index cache, then we want to @@ -627,10 +238,10 @@ static int threeway_merge(struct cache_entry **stages) if (head) { /* #5ALT, #15 */ if (same(head, remote)) - return merged_entry(head, index); + return merged_entry(head, index, o); /* #13, #3ALT */ if (!df_conflict_remote && remote_match && !head_match) - return merged_entry(head, index); + return merged_entry(head, index, o); } /* #1 */ @@ -640,7 +251,7 @@ static int threeway_merge(struct cache_entry **stages) /* Under the new "aggressive" rule, we resolve mostly trivial * cases that we historically had git-merge-one-file resolve. */ - if (aggressive) { + if (o->aggressive) { int head_deleted = !head && !df_conflict_head; int remote_deleted = !remote && !df_conflict_remote; /* @@ -651,35 +262,35 @@ static int threeway_merge(struct cache_entry **stages) (head_deleted && remote && remote_match) || (remote_deleted && head && head_match)) { if (index) - return deleted_entry(index, index); + return deleted_entry(index, index, o); else if (path) - verify_absent(path, "removed"); + verify_absent(path, "removed", o); return 0; } /* * Added in both, identically. */ if (no_anc_exists && head && remote && same(head, remote)) - return merged_entry(head, index); + return merged_entry(head, index, o); } /* Below are "no merge" cases, which require that the index be * up-to-date to avoid the files getting overwritten with - * conflict resolution files. + * conflict resolution files. */ if (index) { - verify_uptodate(index); + verify_uptodate(index, o); } else if (path) - verify_absent(path, "overwritten"); + verify_absent(path, "overwritten", o); - nontrivial_merge = 1; + o->nontrivial_merge = 1; /* #2, #3, #4, #6, #7, #9, #11. */ count = 0; if (!head_match || !remote_match) { - for (i = 1; i < head_idx; i++) { + for (i = 1; i < o->head_idx; i++) { if (stages[i]) { keep_entry(stages[i]); count++; @@ -708,14 +319,15 @@ static int threeway_merge(struct cache_entry **stages) * "carry forward" rule, please see . * */ -static int twoway_merge(struct cache_entry **src) +static int twoway_merge(struct cache_entry **src, + struct unpack_trees_options *o) { struct cache_entry *current = src[0]; struct cache_entry *oldtree = src[1], *newtree = src[2]; - if (merge_size != 2) + if (o->merge_size != 2) return error("Cannot do a twoway merge of %d trees", - merge_size); + o->merge_size); if (current) { if ((!oldtree && !newtree) || /* 4 and 5 */ @@ -730,12 +342,12 @@ static int twoway_merge(struct cache_entry **src) } else if (oldtree && !newtree && same(current, oldtree)) { /* 10 or 11 */ - return deleted_entry(oldtree, current); + return deleted_entry(oldtree, current, o); } else if (oldtree && newtree && same(current, oldtree) && !same(current, newtree)) { /* 20 or 21 */ - return merged_entry(newtree, current); + return merged_entry(newtree, current, o); } else { /* all other failures */ @@ -749,9 +361,9 @@ static int twoway_merge(struct cache_entry **src) } } else if (newtree) - return merged_entry(newtree, current); + return merged_entry(newtree, current, o); else - return deleted_entry(oldtree, current); + return deleted_entry(oldtree, current, o); } /* @@ -760,20 +372,21 @@ static int twoway_merge(struct cache_entry **src) * Keep the index entries at stage0, collapse stage1 but make sure * stage0 does not have anything there. */ -static int bind_merge(struct cache_entry **src) +static int bind_merge(struct cache_entry **src, + struct unpack_trees_options *o) { struct cache_entry *old = src[0]; struct cache_entry *a = src[1]; - if (merge_size != 1) + if (o->merge_size != 1) return error("Cannot do a bind merge of %d trees\n", - merge_size); + o->merge_size); if (a && old) die("Entry '%s' overlaps. Cannot bind.", a->name); if (!a) return keep_entry(old); else - return merged_entry(a, NULL); + return merged_entry(a, NULL, o); } /* @@ -782,19 +395,20 @@ static int bind_merge(struct cache_entry **src) * The rule is: * - take the stat information from stage0, take the data from stage1 */ -static int oneway_merge(struct cache_entry **src) +static int oneway_merge(struct cache_entry **src, + struct unpack_trees_options *o) { struct cache_entry *old = src[0]; struct cache_entry *a = src[1]; - if (merge_size != 1) + if (o->merge_size != 1) return error("Cannot do a oneway merge of %d trees", - merge_size); + o->merge_size); if (!a) - return deleted_entry(old, old); + return deleted_entry(old, old, o); if (old && same(old, a)) { - if (reset) { + if (o->reset) { struct stat st; if (lstat(old->name, &st) || ce_match_stat(old, &st, 1)) @@ -802,7 +416,7 @@ static int oneway_merge(struct cache_entry **src) } return keep_entry(old); } - return merged_entry(a, old); + return merged_entry(a, old, o); } static int read_cache_unmerged(void) @@ -874,14 +488,12 @@ int cmd_read_tree(int argc, const char **argv, const char *prefix) { int i, newfd, stage = 0; unsigned char sha1[20]; - merge_fn_t fn = NULL; + struct unpack_trees_options opts; - df_conflict_list.next = &df_conflict_list; - state.base_dir = ""; - state.force = 1; - state.quiet = 1; - state.refresh_cache = 1; + memset(&opts, 0, sizeof(opts)); + opts.head_idx = -1; + setup_git_directory(); git_config(git_default_config); newfd = hold_lock_file_for_update(&lock_file, get_index_file()); @@ -890,8 +502,6 @@ int cmd_read_tree(int argc, const char **argv, const char *prefix) git_config(git_default_config); - merge = 0; - reset = 0; for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -899,12 +509,12 @@ int cmd_read_tree(int argc, const char **argv, const char *prefix) * the working tree. */ if (!strcmp(arg, "-u")) { - update = 1; + opts.update = 1; continue; } if (!strcmp(arg, "-v")) { - verbose_update = 1; + opts.verbose_update = 1; continue; } @@ -912,7 +522,7 @@ int cmd_read_tree(int argc, const char **argv, const char *prefix) * not even look at the working tree. */ if (!strcmp(arg, "-i")) { - index_only = 1; + opts.index_only = 1; continue; } @@ -921,10 +531,10 @@ int cmd_read_tree(int argc, const char **argv, const char *prefix) * given subdirectory. */ if (!strncmp(arg, "--prefix=", 9)) { - if (stage || merge || prefix) + if (stage || opts.merge || opts.prefix) usage(read_tree_usage); - prefix = arg + 9; - merge = 1; + opts.prefix = arg + 9; + opts.merge = 1; stage = 1; if (read_cache_unmerged()) die("you need to resolve your current index first"); @@ -936,38 +546,38 @@ int cmd_read_tree(int argc, const char **argv, const char *prefix) * correspond to them. */ if (!strcmp(arg, "--reset")) { - if (stage || merge || prefix) + if (stage || opts.merge || opts.prefix) usage(read_tree_usage); - reset = 1; - merge = 1; + opts.reset = 1; + opts.merge = 1; stage = 1; read_cache_unmerged(); continue; } if (!strcmp(arg, "--trivial")) { - trivial_merges_only = 1; + opts.trivial_merges_only = 1; continue; } if (!strcmp(arg, "--aggressive")) { - aggressive = 1; + opts.aggressive = 1; continue; } /* "-m" stands for "merge", meaning we start in stage 1 */ if (!strcmp(arg, "-m")) { - if (stage || merge || prefix) + if (stage || opts.merge || opts.prefix) usage(read_tree_usage); if (read_cache_unmerged()) die("you need to resolve your current index first"); stage = 1; - merge = 1; + opts.merge = 1; continue; } /* using -u and -i at the same time makes no sense */ - if (1 < index_only + update) + if (1 < opts.index_only + opts.update) usage(read_tree_usage); if (get_sha1(arg, sha1)) @@ -976,52 +586,53 @@ int cmd_read_tree(int argc, const char **argv, const char *prefix) die("failed to unpack tree object %s", arg); stage++; } - if ((update||index_only) && !merge) + if ((opts.update||opts.index_only) && !opts.merge) usage(read_tree_usage); - if (prefix) { - int pfxlen = strlen(prefix); + if (opts.prefix) { + int pfxlen = strlen(opts.prefix); int pos; - if (prefix[pfxlen-1] != '/') + if (opts.prefix[pfxlen-1] != '/') die("prefix must end with /"); if (stage != 2) die("binding merge takes only one tree"); - pos = cache_name_pos(prefix, pfxlen); + pos = cache_name_pos(opts.prefix, pfxlen); if (0 <= pos) die("corrupt index file"); pos = -pos-1; if (pos < active_nr && - !strncmp(active_cache[pos]->name, prefix, pfxlen)) - die("subdirectory '%s' already exists.", prefix); - pos = cache_name_pos(prefix, pfxlen-1); + !strncmp(active_cache[pos]->name, opts.prefix, pfxlen)) + die("subdirectory '%s' already exists.", opts.prefix); + pos = cache_name_pos(opts.prefix, pfxlen-1); if (0 <= pos) - die("file '%.*s' already exists.", pfxlen-1, prefix); + die("file '%.*s' already exists.", + pfxlen-1, opts.prefix); } - if (merge) { + if (opts.merge) { if (stage < 2) die("just how do you expect me to merge %d trees?", stage-1); switch (stage - 1) { case 1: - fn = prefix ? bind_merge : oneway_merge; + opts.fn = opts.prefix ? bind_merge : oneway_merge; break; case 2: - fn = twoway_merge; + opts.fn = twoway_merge; break; case 3: default: - fn = threeway_merge; + opts.fn = threeway_merge; cache_tree_free(&active_cache_tree); break; } if (stage - 1 >= 3) - head_idx = stage - 2; + opts.head_idx = stage - 2; else - head_idx = 1; + opts.head_idx = 1; } - unpack_trees(fn); + unpack_trees(trees, &opts); /* * When reading only one tree (either the most basic form, @@ -1029,7 +640,7 @@ int cmd_read_tree(int argc, const char **argv, const char *prefix) * valid cache-tree because the index must match exactly * what came from the tree. */ - if (trees && trees->item && !prefix && (!merge || (stage == 2))) { + if (trees && trees->item && !opts.prefix && (!opts.merge || (stage == 2))) { cache_tree_free(&active_cache_tree); prime_cache_tree(); } diff --git a/unpack-trees.c b/unpack-trees.c new file mode 100644 index 0000000000..20251822e4 --- /dev/null +++ b/unpack-trees.c @@ -0,0 +1,395 @@ +#include +#include +#include "cache.h" +#include "tree.h" +#include "tree-walk.h" +#include "unpack-trees.h" + +struct tree_entry_list { + struct tree_entry_list *next; + unsigned directory : 1; + unsigned executable : 1; + unsigned symlink : 1; + unsigned int mode; + const char *name; + const unsigned char *sha1; +}; + +static struct tree_entry_list *create_tree_entry_list(struct tree *tree) +{ + struct tree_desc desc; + struct name_entry one; + struct tree_entry_list *ret = NULL; + struct tree_entry_list **list_p = &ret; + + desc.buf = tree->buffer; + desc.size = tree->size; + + while (tree_entry(&desc, &one)) { + struct tree_entry_list *entry; + + entry = xmalloc(sizeof(struct tree_entry_list)); + entry->name = one.path; + entry->sha1 = one.sha1; + entry->mode = one.mode; + entry->directory = S_ISDIR(one.mode) != 0; + entry->executable = (one.mode & S_IXUSR) != 0; + entry->symlink = S_ISLNK(one.mode) != 0; + entry->next = NULL; + + *list_p = entry; + list_p = &entry->next; + } + return ret; +} + +static int entcmp(const char *name1, int dir1, const char *name2, int dir2) +{ + int len1 = strlen(name1); + int len2 = strlen(name2); + int len = len1 < len2 ? len1 : len2; + int ret = memcmp(name1, name2, len); + unsigned char c1, c2; + if (ret) + return ret; + c1 = name1[len]; + c2 = name2[len]; + if (!c1 && dir1) + c1 = '/'; + if (!c2 && dir2) + c2 = '/'; + ret = (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0; + if (c1 && c2 && !ret) + ret = len1 - len2; + return ret; +} + +static int unpack_trees_rec(struct tree_entry_list **posns, int len, + const char *base, struct unpack_trees_options *o, + int *indpos, + struct tree_entry_list *df_conflict_list) +{ + int baselen = strlen(base); + int src_size = len + 1; + do { + int i; + const char *first; + int firstdir = 0; + int pathlen; + unsigned ce_size; + struct tree_entry_list **subposns; + struct cache_entry **src; + int any_files = 0; + int any_dirs = 0; + char *cache_name; + int ce_stage; + + /* Find the first name in the input. */ + + first = NULL; + cache_name = NULL; + + /* Check the cache */ + if (o->merge && *indpos < active_nr) { + /* This is a bit tricky: */ + /* If the index has a subdirectory (with + * contents) as the first name, it'll get a + * filename like "foo/bar". But that's after + * "foo", so the entry in trees will get + * handled first, at which point we'll go into + * "foo", and deal with "bar" from the index, + * because the base will be "foo/". The only + * way we can actually have "foo/bar" first of + * all the things is if the trees don't + * contain "foo" at all, in which case we'll + * handle "foo/bar" without going into the + * directory, but that's fine (and will return + * an error anyway, with the added unknown + * file case. + */ + + cache_name = active_cache[*indpos]->name; + if (strlen(cache_name) > baselen && + !memcmp(cache_name, base, baselen)) { + cache_name += baselen; + first = cache_name; + } else { + cache_name = NULL; + } + } + +#if DBRT_DEBUG > 1 + if (first) + printf("index %s\n", first); +#endif + for (i = 0; i < len; i++) { + if (!posns[i] || posns[i] == df_conflict_list) + continue; +#if DBRT_DEBUG > 1 + printf("%d %s\n", i + 1, posns[i]->name); +#endif + if (!first || entcmp(first, firstdir, + posns[i]->name, + posns[i]->directory) > 0) { + first = posns[i]->name; + firstdir = posns[i]->directory; + } + } + /* No name means we're done */ + if (!first) + return 0; + + pathlen = strlen(first); + ce_size = cache_entry_size(baselen + pathlen); + + src = xcalloc(src_size, sizeof(struct cache_entry *)); + + subposns = xcalloc(len, sizeof(struct tree_list_entry *)); + + if (cache_name && !strcmp(cache_name, first)) { + any_files = 1; + src[0] = active_cache[*indpos]; + remove_cache_entry_at(*indpos); + } + + for (i = 0; i < len; i++) { + struct cache_entry *ce; + + if (!posns[i] || + (posns[i] != df_conflict_list && + strcmp(first, posns[i]->name))) { + continue; + } + + if (posns[i] == df_conflict_list) { + src[i + o->merge] = o->df_conflict_entry; + continue; + } + + if (posns[i]->directory) { + struct tree *tree = lookup_tree(posns[i]->sha1); + any_dirs = 1; + parse_tree(tree); + subposns[i] = create_tree_entry_list(tree); + posns[i] = posns[i]->next; + src[i + o->merge] = o->df_conflict_entry; + continue; + } + + if (!o->merge) + ce_stage = 0; + else if (i + 1 < o->head_idx) + ce_stage = 1; + else if (i + 1 > o->head_idx) + ce_stage = 3; + else + ce_stage = 2; + + ce = xcalloc(1, ce_size); + ce->ce_mode = create_ce_mode(posns[i]->mode); + ce->ce_flags = create_ce_flags(baselen + pathlen, + ce_stage); + memcpy(ce->name, base, baselen); + memcpy(ce->name + baselen, first, pathlen + 1); + + any_files = 1; + + memcpy(ce->sha1, posns[i]->sha1, 20); + src[i + o->merge] = ce; + subposns[i] = df_conflict_list; + posns[i] = posns[i]->next; + } + if (any_files) { + if (o->merge) { + int ret; + +#if DBRT_DEBUG > 1 + printf("%s:\n", first); + for (i = 0; i < src_size; i++) { + printf(" %d ", i); + if (src[i]) + printf("%s\n", sha1_to_hex(src[i]->sha1)); + else + printf("\n"); + } +#endif + ret = o->fn(src, o); + +#if DBRT_DEBUG > 1 + printf("Added %d entries\n", ret); +#endif + *indpos += ret; + } else { + for (i = 0; i < src_size; i++) { + if (src[i]) { + add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK); + } + } + } + } + if (any_dirs) { + char *newbase = xmalloc(baselen + 2 + pathlen); + memcpy(newbase, base, baselen); + memcpy(newbase + baselen, first, pathlen); + newbase[baselen + pathlen] = '/'; + newbase[baselen + pathlen + 1] = '\0'; + if (unpack_trees_rec(subposns, len, newbase, o, + indpos, df_conflict_list)) + return -1; + free(newbase); + } + free(subposns); + free(src); + } while (1); +} + +/* Unlink the last component and attempt to remove leading + * directories, in case this unlink is the removal of the + * last entry in the directory -- empty directories are removed. + */ +static void unlink_entry(char *name) +{ + char *cp, *prev; + + if (unlink(name)) + return; + prev = NULL; + while (1) { + int status; + cp = strrchr(name, '/'); + if (prev) + *prev = '/'; + if (!cp) + break; + + *cp = 0; + status = rmdir(name); + if (status) { + *cp = '/'; + break; + } + prev = cp; + } +} + +static volatile int progress_update = 0; + +static void progress_interval(int signum) +{ + progress_update = 1; +} + +static void setup_progress_signal(void) +{ + struct sigaction sa; + struct itimerval v; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = progress_interval; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(SIGALRM, &sa, NULL); + + v.it_interval.tv_sec = 1; + v.it_interval.tv_usec = 0; + v.it_value = v.it_interval; + setitimer(ITIMER_REAL, &v, NULL); +} + +static struct checkout state; +static void check_updates(struct cache_entry **src, int nr, + struct unpack_trees_options *o) +{ + unsigned short mask = htons(CE_UPDATE); + unsigned last_percent = 200, cnt = 0, total = 0; + + if (o->update && o->verbose_update) { + for (total = cnt = 0; cnt < nr; cnt++) { + struct cache_entry *ce = src[cnt]; + if (!ce->ce_mode || ce->ce_flags & mask) + total++; + } + + /* Don't bother doing this for very small updates */ + if (total < 250) + total = 0; + + if (total) { + fprintf(stderr, "Checking files out...\n"); + setup_progress_signal(); + progress_update = 1; + } + cnt = 0; + } + + while (nr--) { + struct cache_entry *ce = *src++; + + if (total) { + if (!ce->ce_mode || ce->ce_flags & mask) { + unsigned percent; + cnt++; + percent = (cnt * 100) / total; + if (percent != last_percent || + progress_update) { + fprintf(stderr, "%4u%% (%u/%u) done\r", + percent, cnt, total); + last_percent = percent; + progress_update = 0; + } + } + } + if (!ce->ce_mode) { + if (o->update) + unlink_entry(ce->name); + continue; + } + if (ce->ce_flags & mask) { + ce->ce_flags &= ~mask; + if (o->update) + checkout_entry(ce, &state, NULL); + } + } + if (total) { + signal(SIGALRM, SIG_IGN); + fputc('\n', stderr); + } +} + +int unpack_trees(struct object_list *trees, struct unpack_trees_options *o) +{ + int indpos = 0; + unsigned len = object_list_length(trees); + struct tree_entry_list **posns; + int i; + struct object_list *posn = trees; + struct tree_entry_list df_conflict_list; + struct cache_entry df_conflict_entry; + + memset(&df_conflict_list, 0, sizeof(df_conflict_list)); + df_conflict_list.next = &df_conflict_list; + state.base_dir = ""; + state.force = 1; + state.quiet = 1; + state.refresh_cache = 1; + + o->merge_size = len; + o->df_conflict_entry = &df_conflict_entry; + + if (len) { + posns = xmalloc(len * sizeof(struct tree_entry_list *)); + for (i = 0; i < len; i++) { + posns[i] = create_tree_entry_list((struct tree *) posn->item); + posn = posn->next; + } + if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "", + o, &indpos, &df_conflict_list)) + return -1; + } + + if (o->trivial_merges_only && o->nontrivial_merge) + die("Merge requires file-level merging"); + + check_updates(active_cache, active_nr, o); + return 0; +} diff --git a/unpack-trees.h b/unpack-trees.h new file mode 100644 index 0000000000..38af89a6b7 --- /dev/null +++ b/unpack-trees.h @@ -0,0 +1,30 @@ +#ifndef UNPACK_TREES_H +#define UNPACK_TREES_H + +struct unpack_trees_options; + +typedef int (*merge_fn_t)(struct cache_entry **src, + struct unpack_trees_options *options); + +struct unpack_trees_options { + int reset; + int merge; + int update; + int index_only; + int nontrivial_merge; + int trivial_merges_only; + int verbose_update; + int aggressive; + const char *prefix; + merge_fn_t fn; + + int head_idx; + int merge_size; + + struct cache_entry *df_conflict_entry; +}; + +extern int unpack_trees(struct object_list *trees, + struct unpack_trees_options *options); + +#endif -- cgit v1.3