{"thread":{"id":"4984","subject":"[PATCH] Make git-mv a builtin","startedAt":"2006-07-26T01:52:35Z","lastAt":"2006-07-26T16:02:33Z","messageCount":6,"participants":["Johannes Schindelin","Junio C Hamano","Josef Weidendorfer"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"24112","messageId":"Pine.LNX.4.63.0607260348130.29667@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"4984","inReplyTo":null,"subject":"[PATCH] Make git-mv a builtin","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-07-26T01:52:35Z","receivedAt":"2006-07-26T01:52:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThis also moves add_file_to_index() to read-cache.c. Oh, and while\ntouching builtin-add.c, it also removes a duplicate git_config() call.\n\nSigned-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n---\n\n\tThere is no test for it, and I am quite certain the old script\n\tdoesn't do it either: git-mv some_tracked_dir/ there/ will\n\tnot work. t7001-mv passes, though.\n\n\tI cannot test, because the script still fails to work here.\n\n Makefile      |    7 +-\n builtin-add.c |   40 ---------\n builtin-mv.c  |  221 +++++++++++++++++++++++++++++++++++++++++++++++++++\n builtin.h     |    1 \n cache.h       |    1 \n git-mv.perl   |  246 ---------------------------------------------------------\n git.c         |    1 \n read-cache.c  |   39 +++++++++\n 8 files changed, 267 insertions(+), 289 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 1069810..baed711 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -165,7 +165,7 @@ SCRIPT_PERL = \\\n \tgit-archimport.perl git-cvsimport.perl git-relink.perl \\\n \tgit-shortlog.perl git-rerere.perl \\\n \tgit-annotate.perl git-cvsserver.perl \\\n-\tgit-svnimport.perl git-mv.perl git-cvsexportcommit.perl \\\n+\tgit-svnimport.perl git-cvsexportcommit.perl \\\n \tgit-send-email.perl git-svn.perl\n \n SCRIPT_PYTHON = \\\n@@ -207,7 +207,7 @@ BUILT_INS = git-log$X git-whatchanged$X \n \tgit-read-tree$X git-commit-tree$X git-write-tree$X \\\n \tgit-apply$X git-show-branch$X git-diff-files$X git-update-index$X \\\n \tgit-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X \\\n-\tgit-fmt-merge-msg$X git-prune$X\n+\tgit-fmt-merge-msg$X git-prune$X git-mv$X\n \n # what 'all' will build and 'install' will install, in gitexecdir\n ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)\n@@ -263,7 +263,8 @@ BUILTIN_OBJS = \\\n \tbuiltin-apply.o builtin-show-branch.o builtin-diff-files.o \\\n \tbuiltin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \\\n \tbuiltin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \\\n-\tbuiltin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o\n+\tbuiltin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o \\\n+\tbuiltin-mv.o path-list.o\n \n GITLIBS = $(LIB_FILE) $(XDIFF_LIB)\n EXTLIBS = -lz\ndiff --git a/builtin-add.c b/builtin-add.c\nindex 2d25698..111d8f5 100644\n--- a/builtin-add.c\n+++ b/builtin-add.c\n@@ -83,45 +83,6 @@ static void fill_directory(struct dir_st\n \t\tprune_directory(dir, pathspec, baselen);\n }\n \n-static int add_file_to_index(const char *path, int verbose)\n-{\n-\tint size, namelen;\n-\tstruct stat st;\n-\tstruct cache_entry *ce;\n-\n-\tif (lstat(path, &st))\n-\t\tdie(\"%s: unable to stat (%s)\", path, strerror(errno));\n-\n-\tif (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))\n-\t\tdie(\"%s: can only add regular files or symbolic links\", path);\n-\n-\tnamelen = strlen(path);\n-\tsize = cache_entry_size(namelen);\n-\tce = xcalloc(1, size);\n-\tmemcpy(ce->name, path, namelen);\n-\tce->ce_flags = htons(namelen);\n-\tfill_stat_cache_info(ce, &st);\n-\n-\tce->ce_mode = create_ce_mode(st.st_mode);\n-\tif (!trust_executable_bit) {\n-\t\t/* If there is an existing entry, pick the mode bits\n-\t\t * from it.\n-\t\t */\n-\t\tint pos = cache_name_pos(path, namelen);\n-\t\tif (pos >= 0)\n-\t\t\tce->ce_mode = active_cache[pos]->ce_mode;\n-\t}\n-\n-\tif (index_path(ce->sha1, path, &st, 1))\n-\t\tdie(\"unable to index file %s\", path);\n-\tif (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD))\n-\t\tdie(\"unable to add %s to index\",path);\n-\tif (verbose)\n-\t\tprintf(\"add '%s'\\n\", path);\n-\tcache_tree_invalidate_path(active_cache_tree, path);\n-\treturn 0;\n-}\n-\n static struct lock_file lock_file;\n \n int cmd_add(int argc, const char **argv, char **envp)\n@@ -160,7 +121,6 @@ int cmd_add(int argc, const char **argv,\n \t\t}\n \t\tdie(builtin_add_usage);\n \t}\n-\tgit_config(git_default_config);\n \tpathspec = get_pathspec(prefix, argv + i);\n \n \tfill_directory(&dir, pathspec);\ndiff --git a/builtin-mv.c b/builtin-mv.c\nnew file mode 100644\nindex 0000000..973bb29\n--- /dev/null\n+++ b/builtin-mv.c\n@@ -0,0 +1,221 @@\n+/*\n+ * \"git mv\" builtin command\n+ *\n+ * Copyright (C) 2006 Johannes Schindelin\n+ */\n+#include <fnmatch.h>\n+\n+#include \"cache.h\"\n+#include \"builtin.h\"\n+#include \"dir.h\"\n+#include \"cache-tree.h\"\n+#include \"path-list.h\"\n+\n+static const char builtin_mv_usage[] =\n+\"git-mv [-n] [-f] { <source> <destination> | [-k] <source>... <destination> }\";\n+\n+static const char **copy_pathspec(const char *prefix, const char **pathspec,\n+\t\tint count, int base_name)\n+{\n+\tconst char **result = xmalloc((count + 1) * sizeof(const char *));\n+\tmemcpy(result, pathspec, count * sizeof(const char *));\n+\tresult[count] = NULL;\n+\tif (base_name) {\n+\t\tint i;\n+\t\tfor (i = 0; i < count; i++) {\n+\t\t\tconst char *last_slash = strrchr(result[i], '/');\n+\t\t\tif (last_slash)\n+\t\t\t\tresult[i] = last_slash + 1;\n+\t\t}\n+\t}\n+\treturn get_pathspec(prefix, result);\n+}\n+\n+static void show_list(const char *label, struct path_list *list)\n+{\n+\tif (list->nr > 0) {\n+\t\tint i;\n+\t\tprintf(\"%s\", label);\n+\t\tfor (i = 0; i < list->nr; i++)\n+\t\t\tprintf(\"%s%s\", i > 0 ? \", \" : \"\", list->items[i].path);\n+\t\tputchar('\\n');\n+\t}\n+}\n+\n+static struct lock_file lock_file;\n+\n+int cmd_mv(int argc, const char **argv, char **envp)\n+{\n+\tint i, newfd, count;\n+\tint verbose = 0, show_only = 0, force = 0, ignore_errors = 0;\n+\tconst char *prefix = setup_git_directory();\n+\tconst char **source, **destination, **dest_path;\n+\tstruct stat st;\n+\tstruct path_list overwritten = {NULL, 0, 0, 0};\n+\tstruct path_list src_for_dst = {NULL, 0, 0, 0};\n+\tstruct path_list added = {NULL, 0, 0, 0};\n+\tstruct path_list deleted = {NULL, 0, 0, 0};\n+\tstruct path_list changed = {NULL, 0, 0, 0};\n+\n+\tgit_config(git_default_config);\n+\n+\tnewfd = hold_lock_file_for_update(&lock_file, get_index_file());\n+\tif (newfd < 0)\n+\t\tdie(\"unable to create new index file\");\n+\n+\tif (read_cache() < 0)\n+\t\tdie(\"index file corrupt\");\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\n+\t\tif (arg[0] != '-')\n+\t\t\tbreak;\n+\t\tif (!strcmp(arg, \"--\")) {\n+\t\t\ti++;\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-n\")) {\n+\t\t\tshow_only = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-f\")) {\n+\t\t\tforce = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-k\")) {\n+\t\t\tignore_errors = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tdie(builtin_mv_usage);\n+\t}\n+\tcount = argc - i - 1;\n+\tif (count < 1)\n+\t\tusage(builtin_mv_usage);\n+\n+\tsource = copy_pathspec(prefix, argv + i, count, 0);\n+\tdest_path = copy_pathspec(prefix, argv + argc - 1, 1, 0);\n+\n+\tif (lstat(dest_path[0], &st) == 0 &&\n+\t\t\tS_ISDIR(st.st_mode))\n+\t\tdestination = copy_pathspec(dest_path[0], argv + i, count, 1);\n+\telse {\n+\t\tif (count != 1)\n+\t\t\tusage(builtin_mv_usage);\n+\t\tdestination = dest_path;\n+\t}\n+\n+\t/* Checking */\n+\tfor (i = 0; i < count; i++) {\n+\t\tconst char *bad = NULL;\n+\n+\t\tif (show_only)\n+\t\t\tprintf(\"Checking rename of '%s' to '%s'\\n\",\n+\t\t\t\tsource[i], destination[i]);\n+\n+\t\tif (lstat(source[i], &st) < 0)\n+\t\t\tbad = \"bad source\";\n+\t\telse if (lstat(destination[i], &st) == 0) {\n+\t\t\tbad = \"destination exists\";\n+\t\t\tif (force) {\n+\t\t\t\t/*\n+\t\t\t\t * only files can overwrite each other:\n+\t\t\t\t * check both source and destination\n+\t\t\t\t */\n+\t\t\t\tif (S_ISREG(st.st_mode)) {\n+\t\t\t\t\tfprintf(stderr, \"Warning: %s;\"\n+\t\t\t\t\t\t\t\" will overwrite!\\n\",\n+\t\t\t\t\t\t\tbad);\n+\t\t\t\t\tbad = NULL;\n+\t\t\t\t\tpath_list_insert(destination[i],\n+\t\t\t\t\t\t\t&overwritten);\n+\t\t\t\t} else\n+\t\t\t\t\tbad = \"Cannot overwrite\";\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (!bad && !strncmp(destination[i], source[i],\n+\t\t\t\t\tstrlen(source[i])))\n+\t\t\tbad = \"can not move directory into itself\";\n+\n+\t\tif (!bad && cache_name_pos(source[i], strlen(source[i])) < 0)\n+\t\t\tbad = \"not under version control\";\n+\n+\t\tif (!bad) {\n+\t\t\tif (path_list_has_path(&src_for_dst, destination[i]))\n+\t\t\t\tbad = \"multiple sources for the same target\";\n+\t\t\telse\n+\t\t\t\tpath_list_insert(destination[i], &src_for_dst);\n+\t\t}\n+\n+\t\tif (bad) {\n+\t\t\tif (ignore_errors) {\n+\t\t\t\tif (--count > 0) {\n+\t\t\t\t\tmemmove(source + i, source + i + 1,\n+\t\t\t\t\t\t(count - i) * sizeof(char *));\n+\t\t\t\t\tmemmove(destination + i,\n+\t\t\t\t\t\t\tdestination + i + 1,\n+\t\t\t\t\t\t\t(count - i) * sizeof(char *));\n+\t\t\t\t}\n+\t\t\t} else\n+\t\t\t\tdie (\"Error: %s, source=%s, destination=%s\",\n+\t\t\t\t\t\tbad, source[i], destination[i]);\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i < count; i++) {\n+\t\tif (show_only || verbose)\n+\t\t\tprintf(\"Renaming %s to %s\\n\",\n+\t\t\t\t\tsource[i], destination[i]);\n+\t\tif (!show_only && rename(source[i], destination[i]) < 0\n+\t\t\t\t&& !ignore_errors)\n+\t\t\tdie (\"renaming %s failed: %s\",\n+\t\t\t\t\tsource[i], strerror(errno));\n+\n+\t\tif (cache_name_pos(source[i], strlen(source[i])) >= 0) {\n+\t\t\tpath_list_insert(source[i], &deleted);\n+\n+\t\t\t/* destination can be a directory with 1 file inside */\n+\t\t\tif (path_list_has_path(&overwritten, destination[i]))\n+\t\t\t\tpath_list_insert(destination[i], &changed);\n+\t\t\telse\n+\t\t\t\tpath_list_insert(destination[i], &added);\n+\t\t} else\n+\t\t\tpath_list_insert(destination[i], &added);\n+\t}\n+\n+        if (show_only) {\n+\t\tshow_list(\"Changed  : \", &changed);\n+\t\tshow_list(\"Adding   : \", &added);\n+\t\tshow_list(\"Deleting : \", &deleted);\n+\t} else {\n+\t\tfor (i = 0; i < changed.nr; i++) {\n+\t\t\tconst char *path = changed.items[i].path;\n+\t\t\tint i = cache_name_pos(path, strlen(path));\n+\t\t\tstruct cache_entry *ce = active_cache[i];\n+\n+\t\t\tif (i < 0)\n+\t\t\t\tdie (\"Huh? Cache entry for %s unknown?\", path);\n+\t\t\trefresh_cache_entry(ce, 0);\n+\t\t}\n+\n+\t\tfor (i = 0; i < added.nr; i++) {\n+\t\t\tconst char *path = added.items[i].path;\n+\t\t\tadd_file_to_index(path, verbose);\n+\t\t}\n+\n+\t\tfor (i = 0; i < deleted.nr; i++) {\n+\t\t\tconst char *path = deleted.items[i].path;\n+\t\t\tremove_file_from_cache(path);\n+\t\t}\n+\n+\t\tif (active_cache_changed) {\n+\t\t\tif (write_cache(newfd, active_cache, active_nr) ||\n+\t\t\t\t\tclose(newfd) ||\n+\t\t\t\t\tcommit_lock_file(&lock_file))\n+\t\t\t\tdie(\"Unable to write new index file\");\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 5339d86..6f3a439 100755\n--- a/builtin.h\n+++ b/builtin.h\n@@ -52,6 +52,7 @@ extern int cmd_rev_parse(int argc, const\n extern int cmd_update_index(int argc, const char **argv, char **envp);\n extern int cmd_update_ref(int argc, const char **argv, char **envp);\n extern int cmd_fmt_merge_msg(int argc, const char **argv, char **envp);\n+extern int cmd_mv(int argc, const char **argv, char **envp);\n \n extern int cmd_write_tree(int argc, const char **argv, char **envp);\n extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);\ndiff --git a/cache.h b/cache.h\nindex 457d1d0..3288b11 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -158,6 +158,7 @@ extern int add_cache_entry(struct cache_\n extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);\n extern int remove_cache_entry_at(int pos);\n extern int remove_file_from_cache(const char *path);\n+extern int add_file_to_index(const char *path, int verbose);\n extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);\n extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int);\n extern int ce_modified(struct cache_entry *ce, struct stat *st, int);\ndiff --git a/git-mv.perl b/git-mv.perl\ndeleted file mode 100755\nindex 322b9fd..0000000\n--- a/git-mv.perl\n+++ /dev/null\n@@ -1,246 +0,0 @@\n-#!/usr/bin/perl\n-#\n-# Copyright 2005, Ryan Anderson <ryan@michonline.com>\n-#                 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>\n-#\n-# This file is licensed under the GPL v2, or a later version\n-# at the discretion of Linus Torvalds.\n-\n-use warnings;\n-use strict;\n-use Getopt::Std;\n-use Git;\n-\n-sub usage() {\n-\tprint <<EOT;\n-$0 [-f] [-n] <source> <destination>\n-$0 [-f] [-n] [-k] <source> ... <destination directory>\n-EOT\n-\texit(1);\n-}\n-\n-our ($opt_n, $opt_f, $opt_h, $opt_k, $opt_v);\n-getopts(\"hnfkv\") || usage;\n-usage() if $opt_h;\n-@ARGV >= 1 or usage;\n-\n-my $repo = Git->repository();\n-\n-my (@srcArgs, @dstArgs, @srcs, @dsts);\n-my ($src, $dst, $base, $dstDir);\n-\n-# remove any trailing slash in arguments\n-for (@ARGV) { s/\\/*$//; }\n-\n-my $argCount = scalar @ARGV;\n-if (-d $ARGV[$argCount-1]) {\n-\t$dstDir = $ARGV[$argCount-1];\n-\t@srcArgs = @ARGV[0..$argCount-2];\n-\n-\tforeach $src (@srcArgs) {\n-\t\t$base = $src;\n-\t\t$base =~ s/^.*\\///;\n-\t\t$dst = \"$dstDir/\". $base;\n-\t\tpush @dstArgs, $dst;\n-\t}\n-}\n-else {\n-    if ($argCount < 2) {\n-\tprint \"Error: need at least two arguments\\n\";\n-\texit(1);\n-    }\n-    if ($argCount > 2) {\n-\tprint \"Error: moving to directory '\"\n-\t    . $ARGV[$argCount-1]\n-\t    . \"' not possible; not existing\\n\";\n-\texit(1);\n-    }\n-    @srcArgs = ($ARGV[0]);\n-    @dstArgs = ($ARGV[1]);\n-    $dstDir = \"\";\n-}\n-\n-my $subdir_prefix = $repo->wc_subdir();\n-\n-# run in git base directory, so that git-ls-files lists all revisioned files\n-chdir $repo->wc_path();\n-$repo->wc_chdir('');\n-\n-# normalize paths, needed to compare against versioned files and update-index\n-# also, this is nicer to end-users by doing \".//a/./b/.//./c\" ==> \"a/b/c\"\n-for (@srcArgs, @dstArgs) {\n-    # prepend git prefix as we run from base directory\n-    $_ = $subdir_prefix.$_;\n-    s|^\\./||;\n-    s|/\\./|/| while (m|/\\./|);\n-    s|//+|/|g;\n-    # Also \"a/b/../c\" ==> \"a/c\"\n-    1 while (s,(^|/)[^/]+/\\.\\./,$1,);\n-}\n-\n-my (@allfiles,@srcfiles,@dstfiles);\n-my $safesrc;\n-my (%overwritten, %srcForDst);\n-\n-{\n-\tlocal $/ = \"\\0\";\n-\t@allfiles = $repo->command('ls-files', '-z');\n-}\n-\n-\n-my ($i, $bad);\n-while(scalar @srcArgs > 0) {\n-    $src = shift @srcArgs;\n-    $dst = shift @dstArgs;\n-    $bad = \"\";\n-\n-    for ($src, $dst) {\n-\t# Be nicer to end-users by doing \".//a/./b/.//./c\" ==> \"a/b/c\"\n-\ts|^\\./||;\n-\ts|/\\./|/| while (m|/\\./|);\n-\ts|//+|/|g;\n-\t# Also \"a/b/../c\" ==> \"a/c\"\n-\t1 while (s,(^|/)[^/]+/\\.\\./,$1,);\n-    }\n-\n-    if ($opt_v) {\n-\tprint \"Checking rename of '$src' to '$dst'\\n\";\n-    }\n-\n-    unless (-f $src || -l $src || -d $src) {\n-\t$bad = \"bad source '$src'\";\n-    }\n-\n-    $safesrc = quotemeta($src);\n-    @srcfiles = grep /^$safesrc(\\/|$)/, @allfiles;\n-\n-    $overwritten{$dst} = 0;\n-    if (($bad eq \"\") && -e $dst) {\n-\t$bad = \"destination '$dst' already exists\";\n-\tif ($opt_f) {\n-\t    # only files can overwrite each other: check both source and destination\n-\t    if (-f $dst && (scalar @srcfiles == 1)) {\n-\t\tprint \"Warning: $bad; will overwrite!\\n\";\n-\t\t$bad = \"\";\n-\t\t$overwritten{$dst} = 1;\n-\t    }\n-\t    else {\n-\t\t$bad = \"Can not overwrite '$src' with '$dst'\";\n-\t    }\n-\t}\n-    }\n-    \n-    if (($bad eq \"\") && ($dst =~ /^$safesrc\\//)) {\n-\t$bad = \"can not move directory '$src' into itself\";\n-    }\n-\n-    if ($bad eq \"\") {\n-        if (scalar @srcfiles == 0) {\n-\t    $bad = \"'$src' not under version control\";\n-\t}\n-    }\n-\n-    if ($bad eq \"\") {\n-       if (defined $srcForDst{$dst}) {\n-           $bad = \"can not move '$src' to '$dst'; already target of \";\n-           $bad .= \"'\".$srcForDst{$dst}.\"'\";\n-       }\n-       else {\n-           $srcForDst{$dst} = $src;\n-       }\n-    }\n-\n-    if ($bad ne \"\") {\n-\tif ($opt_k) {\n-\t    print \"Warning: $bad; skipping\\n\";\n-\t    next;\n-\t}\n-\tprint \"Error: $bad\\n\";\n-\texit(1);\n-    }\n-    push @srcs, $src;\n-    push @dsts, $dst;\n-}\n-\n-# Final pass: rename/move\n-my (@deletedfiles,@addedfiles,@changedfiles);\n-$bad = \"\";\n-while(scalar @srcs > 0) {\n-    $src = shift @srcs;\n-    $dst = shift @dsts;\n-\n-    if ($opt_n || $opt_v) { print \"Renaming $src to $dst\\n\"; }\n-    if (!$opt_n) {\n-\tif (!rename($src,$dst)) {\n-\t    $bad = \"renaming '$src' failed: $!\";\n-\t    if ($opt_k) {\n-\t\tprint \"Warning: skipped: $bad\\n\";\n-\t\t$bad = \"\";\n-\t\tnext;\n-\t    }\n-\t    last;\n-\t}\n-    }\n-\n-    $safesrc = quotemeta($src);\n-    @srcfiles = grep /^$safesrc(\\/|$)/, @allfiles;\n-    @dstfiles = @srcfiles;\n-    s/^$safesrc(\\/|$)/$dst$1/ for @dstfiles;\n-\n-    push @deletedfiles, @srcfiles;\n-    if (scalar @srcfiles == 1) {\n-\t# $dst can be a directory with 1 file inside\n-\tif ($overwritten{$dst} ==1) {\n-\t    push @changedfiles, $dstfiles[0];\n-\n-\t} else {\n-\t    push @addedfiles, $dstfiles[0];\n-\t}\n-    }\n-    else {\n-\tpush @addedfiles, @dstfiles;\n-    }\n-}\n-\n-if ($opt_n) {\n-    if (@changedfiles) {\n-\tprint \"Changed  : \". join(\", \", @changedfiles) .\"\\n\";\n-    }\n-    if (@addedfiles) {\n-\tprint \"Adding   : \". join(\", \", @addedfiles) .\"\\n\";\n-    }\n-    if (@deletedfiles) {\n-\tprint \"Deleting : \". join(\", \", @deletedfiles) .\"\\n\";\n-    }\n-}\n-else {\n-    if (@changedfiles) {\n-\tmy ($fd, $ctx) = $repo->command_input_pipe('update-index', '-z', '--stdin');\n-\tforeach my $fileName (@changedfiles) {\n-\t\tprint $fd \"$fileName\\0\";\n-\t}\n-\tgit_cmd_try { $repo->command_close_pipe($fd, $ctx); }\n-\t\t'git-update-index failed to update changed files with code %d';\n-    }\n-    if (@addedfiles) {\n-\tmy ($fd, $ctx) = $repo->command_input_pipe('update-index', '--add', '-z', '--stdin');\n-\tforeach my $fileName (@addedfiles) {\n-\t\tprint $fd \"$fileName\\0\";\n-\t}\n-\tgit_cmd_try { $repo->command_close_pipe($fd, $ctx); }\n-\t\t'git-update-index failed to add new files with code %d';\n-    }\n-    if (@deletedfiles) {\n-\tmy ($fd, $ctx) = $repo->command_input_pipe('update-index', '--remove', '-z', '--stdin');\n-\tforeach my $fileName (@deletedfiles) {\n-\t\tprint $fd \"$fileName\\0\";\n-\t}\n-\tgit_cmd_try { $repo->command_close_pipe($fd, $ctx); }\n-\t\t'git-update-index failed to remove old files with code %d';\n-    }\n-}\n-\n-if ($bad ne \"\") {\n-    print \"Error: $bad\\n\";\n-    exit(1);\n-}\ndiff --git a/git.c b/git.c\nindex e4b2174..bd58885 100644\n--- a/git.c\n+++ b/git.c\n@@ -261,6 +261,7 @@ static void handle_internal_command(int \n \t\t{ \"update-ref\", cmd_update_ref },\n \t\t{ \"fmt-merge-msg\", cmd_fmt_merge_msg },\n \t\t{ \"prune\", cmd_prune },\n+\t\t{ \"mv\", cmd_mv },\n \t};\n \tint i;\n \ndiff --git a/read-cache.c b/read-cache.c\nindex 9c0a9fc..c375e91 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -319,6 +319,45 @@ int remove_file_from_cache(const char *p\n \treturn 0;\n }\n \n+int add_file_to_index(const char *path, int verbose)\n+{\n+\tint size, namelen;\n+\tstruct stat st;\n+\tstruct cache_entry *ce;\n+\n+\tif (lstat(path, &st))\n+\t\tdie(\"%s: unable to stat (%s)\", path, strerror(errno));\n+\n+\tif (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))\n+\t\tdie(\"%s: can only add regular files or symbolic links\", path);\n+\n+\tnamelen = strlen(path);\n+\tsize = cache_entry_size(namelen);\n+\tce = xcalloc(1, size);\n+\tmemcpy(ce->name, path, namelen);\n+\tce->ce_flags = htons(namelen);\n+\tfill_stat_cache_info(ce, &st);\n+\n+\tce->ce_mode = create_ce_mode(st.st_mode);\n+\tif (!trust_executable_bit) {\n+\t\t/* If there is an existing entry, pick the mode bits\n+\t\t * from it.\n+\t\t */\n+\t\tint pos = cache_name_pos(path, namelen);\n+\t\tif (pos >= 0)\n+\t\t\tce->ce_mode = active_cache[pos]->ce_mode;\n+\t}\n+\n+\tif (index_path(ce->sha1, path, &st, 1))\n+\t\tdie(\"unable to index file %s\", path);\n+\tif (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD))\n+\t\tdie(\"unable to add %s to index\",path);\n+\tif (verbose)\n+\t\tprintf(\"add '%s'\\n\", path);\n+\tcache_tree_invalidate_path(active_cache_tree, path);\n+\treturn 0;\n+}\n+\n int ce_same_name(struct cache_entry *a, struct cache_entry *b)\n {\n \tint len = ce_namelen(a);\n-- \n1.4.2.rc2.g39be8\n"},{"id":"24123","messageId":"7vk660udcn.fsf@assigned-by-dhcp.cox.net","threadId":"4984","inReplyTo":"Pine.LNX.4.63.0607260348130.29667@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] Make git-mv a builtin","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-07-26T05:47:20Z","receivedAt":"2006-07-26T05:47:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> This also moves add_file_to_index() to read-cache.c. Oh, and while\n> touching builtin-add.c, it also removes a duplicate git_config() call.\n\nI'd have preferred to see this not depend on uncooked\nmerge-recur infrastructure, since it is very likely that this\nwould be ready to graduate to \"master\" before merge-recur.\n\nI've branched from \"master\" and placed the path-list stuff which\nwas split out of the merge-recur WIP, and applied this patch\nwith some trivial adjustment, and will place it on \"pu\", so I'd\nappreciate if you can double check the result.\n\n> diff --git a/builtin.h b/builtin.h\n> index 5339d86..6f3a439 100755\n\nHuh -- 100755?\n"},{"id":"24126","messageId":"Pine.LNX.4.63.0607261204020.29667@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"4984","inReplyTo":"7vk660udcn.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Make git-mv a builtin","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-07-26T10:19:17Z","receivedAt":"2006-07-26T10:19:17Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 25 Jul 2006, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > This also moves add_file_to_index() to read-cache.c. Oh, and while\n> > touching builtin-add.c, it also removes a duplicate git_config() call.\n> \n> I'd have preferred to see this not depend on uncooked\n> merge-recur infrastructure, since it is very likely that this\n> would be ready to graduate to \"master\" before merge-recur.\n\nI use \"next\" to base all my work (git or otherwise) on, and forgot that \npath-list did not yet make it into \"master\". Sorry.\n\n> I've branched from \"master\" and placed the path-list stuff which\n> was split out of the merge-recur WIP, and applied this patch\n> with some trivial adjustment, and will place it on \"pu\", so I'd\n> appreciate if you can double check the result.\n\nLooks good from what I see. BTW do you have any indent command line you \nprefer? I see that all changes but in the usage string (which was my \nfault: thank you), and in Makefile (putting path-list into libgit.a rather \nthan linking it specifically), are white space changes.\n\n> > diff --git a/builtin.h b/builtin.h\n> > index 5339d86..6f3a439 100755\n> \n> Huh -- 100755?\n\nA mistake introduced on cygwin. Again, sorry.\n\nCiao,\nDscho\n"},{"id":"24128","messageId":"200607261544.08435.Josef.Weidendorfer@gmx.de","threadId":"4984","inReplyTo":"Pine.LNX.4.63.0607260348130.29667@wbgn013.biozentrum.uni-wuerzburg.de","subject":"[PATCH] Extend testing git-mv for renaming of subdirectories","fromName":"Josef Weidendorfer","fromEmail":"josef.weidendorfer@gmx.de","sentAt":"2006-07-26T13:44:08Z","receivedAt":"2006-07-26T13:44:08Z","isPatch":true,"sender":{"key":"josef.weidendorfer@gmx.de","avatar":null},"body":"Signed-off-by: Josef Weidendorfer <Josef.Weidendorfer@gmx.de>\n---\n\nOn Wednesday 26 July 2006 03:52, Johannes Schindelin wrote:\n> \tThere is no test for it, and I am quite certain the old script\n> \tdoesn't do it either: git-mv some_tracked_dir/ there/ will\n> \tnot work. t7001-mv passes, though.\n\nHmm... Renaming full subtrees worked since the old git-rename days.\nI just checked it, and it works fine.\n\nMy bad, that there was no test for this, so what about this?\n\nJosef\n\n t/t7001-mv.sh |   24 ++++++++++++++++++++++++\n 1 files changed, 24 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t7001-mv.sh b/t/t7001-mv.sh\nindex 811a479..9270a41 100755\n--- a/t/t7001-mv.sh\n+++ b/t/t7001-mv.sh\n@@ -38,4 +38,28 @@ test_expect_success \\\n     'git-diff-tree -r -M --name-status  HEAD^ HEAD | \\\n     grep -E \"^R100.+path1/COPYING.+path0/COPYING\"'\n \n+test_expect_success \\\n+    'adding another file' \\\n+    'cp ../../README path0/README &&\n+     git-add path0/README &&\n+     git-commit -m add2 -a'\n+\n+test_expect_success \\\n+    'moving whole subdirectory' \\\n+    'git-mv path0 path2'\n+\n+test_expect_success \\\n+    'commiting the change' \\\n+    'git-commit -m dir-move -a'\n+\n+test_expect_success \\\n+    'checking the commit' \\\n+    'git-diff-tree -r -M --name-status  HEAD^ HEAD | \\\n+     grep -E \"^R100.+path0/COPYING.+path2/COPYING\" &&\n+     git-diff-tree -r -M --name-status  HEAD^ HEAD | \\\n+     grep -E \"^R100.+path0/README.+path2/README\"'\n+\n test_done\n+\n-- \n1.4.2.rc1.g791e\n"},{"id":"24138","messageId":"Pine.LNX.4.63.0607261721460.29667@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"4984","inReplyTo":"200607261544.08435.Josef.Weidendorfer@gmx.de","subject":"Re: [PATCH] Extend testing git-mv for renaming of subdirectories","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-07-26T15:22:44Z","receivedAt":"2006-07-26T15:22:44Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 26 Jul 2006, Josef Weidendorfer wrote:\n\n> On Wednesday 26 July 2006 03:52, Johannes Schindelin wrote:\n> > \tThere is no test for it, and I am quite certain the old script\n> > \tdoesn't do it either: git-mv some_tracked_dir/ there/ will\n> > \tnot work. t7001-mv passes, though.\n> \n> Hmm... Renaming full subtrees worked since the old git-rename days.\n> I just checked it, and it works fine.\n\nThanks.\n\nAnd thanks again: since there is a test now, I'll have to implement that \nfeature, too, I guess ;-)\n"},{"id":"24140","messageId":"200607261802.33757.Josef.Weidendorfer@gmx.de","threadId":"4984","inReplyTo":"Pine.LNX.4.63.0607261721460.29667@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] Extend testing git-mv for renaming of subdirectories","fromName":"Josef Weidendorfer","fromEmail":"josef.weidendorfer@gmx.de","sentAt":"2006-07-26T16:02:33Z","receivedAt":"2006-07-26T16:02:33Z","isPatch":true,"sender":{"key":"josef.weidendorfer@gmx.de","avatar":null},"body":"On Wednesday 26 July 2006 17:22, Johannes Schindelin wrote:\n> > Hmm... Renaming full subtrees worked since the old git-rename days.\n> > I just checked it, and it works fine.\n> \n> Thanks.\n> \n> And thanks again: since there is a test now, I'll have to implement that \n> feature, too, I guess ;-)\n\nSorry about that, but that was the reason for this patch ;-)\nI think it would be too bad for users to have this as regression.\n\nWhen I first looked at the git-rename script, it wasn't clear to me\nthat moving dirs actually works. The thing is that PERL's \"rename\"\ncan move subdirectories. And on the git side, it does a grep with the\ndir name against the list of tracked files, so we catch all tracked\nfiles in the subdirectory.\n\nJosef\n"}]}