{"thread":{"id":"11858","subject":"What about git cp ?","startedAt":"2008-02-03T18:23:12Z","lastAt":"2008-02-11T13:43:12Z","messageCount":14,"participants":["Francis Moreau","Matthieu Moy","Remi Vanicat","Jakub Narebski","Miklos Vajna","Johannes Schindelin","Junio C Hamano","Robin Rosenberg"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"67245","messageId":"38b2ab8a0802031023y2ed7a5aax6d3c404b08757a4d@mail.gmail.com","threadId":"11858","inReplyTo":null,"subject":"What about git cp ?","fromName":"Francis Moreau","fromEmail":"francis.moro@gmail.com","sentAt":"2008-02-03T18:23:12Z","receivedAt":"2008-02-03T18:23:12Z","isPatch":false,"sender":{"key":"francis.moro@gmail.com","avatar":null},"body":"Hello,\n\nI'm looking for a something which could be done by a git-cp command.\n\nI'd like to copy a file with its history to a new  file but want to keep the old\none,\n\nThanks\n-- \nFrancis\n"},{"id":"67248","messageId":"vpq63x5swsj.fsf@bauges.imag.fr","threadId":"11858","inReplyTo":"38b2ab8a0802031023y2ed7a5aax6d3c404b08757a4d@mail.gmail.com","subject":"Re: What about git cp ?","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2008-02-03T18:46:20Z","receivedAt":"2008-02-03T18:46:20Z","isPatch":false,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"\"Francis Moreau\" <francis.moro@gmail.com> writes:\n\n> Hello,\n>\n> I'm looking for a something which could be done by a git-cp command.\n>\n> I'd like to copy a file with its history to a new  file but want to keep the old\n> one,\n\nGit doesn't _record_ copies and renames, but detects them\nafter-the-fact (either by default, or explicitly like \"git blame -C\"\nor so).\n\nSo, just \"cp + git add\" and you're done. That's what a \"git cp\"\ncommand would do if it existed.\n\n-- \nMatthieu\n"},{"id":"67250","messageId":"87hcgplvjh.dlv@maison.homelinux.org","threadId":"11858","inReplyTo":"38b2ab8a0802031023y2ed7a5aax6d3c404b08757a4d@mail.gmail.com","subject":"Re: What about git cp ?","fromName":"Remi Vanicat","fromEmail":"vanicat@debian.org","sentAt":"2008-02-03T18:55:14Z","receivedAt":"2008-02-03T18:55:14Z","isPatch":false,"sender":{"key":"vanicat@debian.org","avatar":"https://gravatar.com/avatar/cd491a7f4c221349809a60f88fc21326b97cce2a705e318898aa74851db92409?d=mp&s=160"},"body":"\"Francis Moreau\" <francis.moro@gmail.com> writes:\n\n> Hello,\n>\n> I'm looking for a something which could be done by a git-cp command.\n>\n> I'd like to copy a file with its history to a new  file but want to\n> keep the old one,\n\nGit don't attach history to a file, but to a content. Just do a:\ncp foo bar\ngit add bar\ngit commit -m \"copying foo to bar\"\n\nAnd it will be done, and when needed, git will be able to know that\nthis was a copy.\n\n-- \nRémi Vanicat\n"},{"id":"67251","messageId":"m3tzkp6eqz.fsf@localhost.localdomain","threadId":"11858","inReplyTo":"vpq63x5swsj.fsf@bauges.imag.fr","subject":"Re: What about git cp ?","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2008-02-03T19:07:10Z","receivedAt":"2008-02-03T19:07:10Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Matthieu Moy <Matthieu.Moy@imag.fr> writes:\n\n> \"Francis Moreau\" <francis.moro@gmail.com> writes:\n> \n> > Hello,\n> >\n> > I'm looking for a something which could be done by a git-cp\n> > command.\n> >\n> > I'd like to copy a file with its history to a new file but want to\n> > keep the old one,\n> \n> Git doesn't _record_ copies and renames, but detects them\n> after-the-fact (either by default, or explicitly like \"git blame -C\"\n> or so).\n> \n> So, just \"cp + git add\" and you're done. That's what a \"git cp\"\n> command would do if it existed.\n\nYou can just put git-cp shell script doing this in PATH (or\nGIT_EXEC_PATH if they are different) for \"git cp\" to just work.\nOr you can write builtin-cp.c, following builtin-mv.c\n\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"68138","messageId":"20080210011203.GL25954@genesis.frugalware.org","threadId":"11858","inReplyTo":"m3tzkp6eqz.fsf@localhost.localdomain","subject":"[RFC/PATCH] Implement git-cp.","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-02-10T01:12:03Z","receivedAt":"2008-02-10T01:12:03Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"Actually it adds a -c option to git-mv to copy instead of move and add a\nbuiltin alias git-cp for 'git mv -c'.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n\nOn Sun, Feb 03, 2008 at 11:07:10AM -0800, Jakub Narebski <jnareb@gmail.com> wrote:\n> You can just put git-cp shell script doing this in PATH (or\n> GIT_EXEC_PATH if they are different) for \"git cp\" to just work.\n> Or you can write builtin-cp.c, following builtin-mv.c\n\nsomething like this?\n\n .gitignore                   |    1 +\n Documentation/git-cp.txt     |   42 +++++++++++++++++\n Documentation/git-mv.txt     |   12 +----\n Documentation/mv-options.txt |    9 ++++\n Makefile                     |    1 +\n builtin-cp.c                 |   24 ++++++++++\n builtin-mv.c                 |  104 ++++++++++++++++++++++++++++++++++++++++--\n builtin.h                    |    1 +\n command-list.txt             |    1 +\n git.c                        |    1 +\n t/t7006-cp.sh                |   75 ++++++++++++++++++++++++++++++\n 11 files changed, 257 insertions(+), 14 deletions(-)\n create mode 100644 Documentation/git-cp.txt\n create mode 100644 Documentation/mv-options.txt\n create mode 100644 builtin-cp.c\n create mode 100755 t/t7006-cp.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex 7f8421d..3d1dcdd 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -26,6 +26,7 @@ git-commit\n git-commit-tree\n git-config\n git-count-objects\n+git-cp\n git-cvsexportcommit\n git-cvsimport\n git-cvsserver\ndiff --git a/Documentation/git-cp.txt b/Documentation/git-cp.txt\nnew file mode 100644\nindex 0000000..3109d04\n--- /dev/null\n+++ b/Documentation/git-cp.txt\n@@ -0,0 +1,42 @@\n+git-cp(1)\n+=========\n+\n+NAME\n+----\n+git-cp - Copy a file, a directory, or a symlink\n+\n+SYNOPSIS\n+--------\n+'git-cp' <options>... <args>...\n+\n+DESCRIPTION\n+-----------\n+This script is used to copy a file, directory or symlink.\n+\n+ git-cp [-f] [-n] <source> <destination>\n+ git-cp [-f] [-n] [-k] <source> ... <destination directory>\n+\n+In the first form, it copies <source>, which must exist and be either\n+a file, symlink or directory, to <destination>.\n+In the second form, the last argument has to be an existing\n+directory; the given sources will be copied into this directory.\n+\n+The index is updated after successful completion, but the change must still be\n+committed.\n+\n+\n+OPTIONS\n+-------\n+include::mv-options.txt[]\n+\n+SEE ALSO\n+--------\n+linkgit:git-mv[1]\n+\n+AUTHOR\n+------\n+Written by Miklos Vajna <vmiklos@frugalware.org>.\n+\n+GIT\n+---\n+Part of the linkgit:git[7] suite\ndiff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt\nindex bff3fbe..45d8255 100644\n--- a/Documentation/git-mv.txt\n+++ b/Documentation/git-mv.txt\n@@ -27,15 +27,9 @@ committed.\n \n OPTIONS\n -------\n--f::\n-\tForce renaming or moving of a file even if the target exists\n--k::\n-        Skip move or rename actions which would lead to an error\n-\tcondition. An error happens when a source is neither existing nor\n-        controlled by GIT, or when it would overwrite an existing\n-        file unless '-f' is given.\n--n, \\--dry-run::\n-\tDo nothing; only show what would happen\n+include::mv-options.txt[]\n+-c::\n+\tCopy instead of move.\n \n \n Author\ndiff --git a/Documentation/mv-options.txt b/Documentation/mv-options.txt\nnew file mode 100644\nindex 0000000..ad78d1e\n--- /dev/null\n+++ b/Documentation/mv-options.txt\n@@ -0,0 +1,9 @@\n+-f::\n+\tForce renaming or moving of a file even if the target exists\n+-k::\n+        Skip move or rename actions which would lead to an error\n+\tcondition. An error happens when a source is neither existing nor\n+        controlled by GIT, or when it would overwrite an existing\n+        file unless '-f' is given.\n+-n, \\--dry-run::\n+\tDo nothing; only show what would happen\ndiff --git a/Makefile b/Makefile\nindex 92341c4..0dd8e02 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -338,6 +338,7 @@ BUILTIN_OBJS = \\\n \tbuiltin-commit.o \\\n \tbuiltin-commit-tree.o \\\n \tbuiltin-count-objects.o \\\n+\tbuiltin-cp.o \\\n \tbuiltin-describe.o \\\n \tbuiltin-diff.o \\\n \tbuiltin-diff-files.o \\\ndiff --git a/builtin-cp.c b/builtin-cp.c\nnew file mode 100644\nindex 0000000..f66daba\n--- /dev/null\n+++ b/builtin-cp.c\n@@ -0,0 +1,24 @@\n+/*\n+ * \"git cp\" builtin alias\n+ *\n+ * Copyright (C) 2008 Miklos Vajna\n+ */\n+#include \"git-compat-util.h\"\n+#include \"builtin.h\"\n+\n+int cmd_cp(int argc, const char **argv, const char *prefix)\n+{\n+\tconst char **nargv;\n+\tint i;\n+\tnargv = xmalloc(sizeof(char *) * (argc + 2));\n+\n+\tnargv[0] = \"mv\";\n+\tnargv[1] = \"-c\";\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tnargv[i+1] = argv[i];\n+\t}\n+\tnargv[argc + 1] = NULL;\n+\n+\treturn cmd_mv(argc + 1, nargv, prefix);\n+}\ndiff --git a/builtin-mv.c b/builtin-mv.c\nindex 990e213..8c069a5 100644\n--- a/builtin-mv.c\n+++ b/builtin-mv.c\n@@ -60,14 +60,103 @@ static const char *add_slash(const char *path)\n \treturn path;\n }\n \n+static int copyfile(const char *src_name, const char *dst_name)\n+{\n+\tint ifd;\n+\tint ofd;\n+\tstruct stat sb;\n+\tint cnt;\n+\tchar buf[1024];\n+\n+\tstat(src_name, &sb);\n+\tif ((ifd = open (src_name, O_RDONLY)) < 0)\n+\t\tdie(\"could not open file %s for reading: %s\", src_name, strerror(errno));\n+\tif ((ofd =\n+\t     open (dst_name, O_WRONLY | O_CREAT | O_TRUNC, 0)) < 0\n+\t    || chmod (dst_name, sb.st_mode & 07777))\n+\t\tdie(\"could not open file %s for writing: %s\", dst_name, strerror(errno));\n+\twhile ((cnt = read (ifd, buf, sizeof buf)) > 0) {\n+\t\tif (write (ofd, buf, cnt) != cnt)\n+\t\t\tdie(\"could not write to file %s\", dst_name);\n+\t}\n+\tclose (ifd);\n+\tclose (ofd);\n+\treturn 0;\n+}\n+\n+static int copytree (const char *src_root, const char *dst_root)\n+{\n+\tchar src_name[1024];\n+\tchar dst_name[1024];\n+\tint err = 0;\n+\tstruct dirent *ent;\n+\tstruct stat sb;\n+\tDIR *dir;\n+\n+\tlstat(src_root, &sb);\n+\n+\tif (!(dir = opendir (src_root)))\n+\t\treturn copyfile(src_root, dst_root);\n+\telse\n+\t\tmkdir(dst_root, sb.st_mode);\n+\n+\twhile ((ent = readdir (dir))) {\n+\n+\t\tif (strcmp (ent->d_name, \".\") == 0 ||\n+\t\t    strcmp (ent->d_name, \"..\") == 0)\n+\t\t\tcontinue;\n+\n+\t\tsnprintf (src_name, sizeof src_name, \"%s/%s\", src_root,\n+\t\t\t  ent->d_name);\n+\n+\t\tsnprintf (dst_name, sizeof dst_name, \"%s/%s\", dst_root,\n+\t\t\t  ent->d_name);\n+\n+\t\tif (lstat (src_name, &sb) < 0)\n+\t\t\tcontinue;\n+\n+\t\tif (S_ISDIR (sb.st_mode)) {\n+\n+\t\t\tif (mkdir (dst_name, sb.st_mode)\n+\t\t\t    || chmod (dst_name, sb.st_mode & 07777)\n+\t\t\t    || copytree (src_name, dst_name)) {\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (S_ISLNK (sb.st_mode)) {\n+\t\t\tchar oldlink[1024];\n+\t\t\tchar dummy[1024];\n+\t\t\tint len;\n+\n+\t\t\tif ((len =\n+\t\t\t     readlink (src_name, oldlink,\n+\t\t\t\t       sizeof (oldlink) - 1)) < 0)\n+\t\t\t\tdie(\"could not read symlink %s: %s\", src_name, strerror(errno));\n+\t\t\toldlink[len] = '\\0'; /* readlink() does not NULL-terminate */\n+\t\t\tif (symlink (oldlink, dst_name))\n+\t\t\t\tdie(\"could not create symlink %s: %s\", dst_name, strerror(errno));\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (copyfile(src_name, dst_name) == -1)\n+\t\t\tbreak;\n+\t}\n+\tclosedir (dir);\n+\n+\treturn 0;\n+}\n+\n static struct lock_file lock_file;\n \n int cmd_mv(int argc, const char **argv, const char *prefix)\n {\n \tint i, newfd;\n-\tint verbose = 0, show_only = 0, force = 0, ignore_errors = 0;\n+\tint verbose = 0, show_only = 0, copy = 0, force = 0, ignore_errors = 0;\n \tstruct option builtin_mv_options[] = {\n \t\tOPT__DRY_RUN(&show_only),\n+\t\tOPT_BOOLEAN('c', NULL, &copy, \"copy instead of move/rename\"),\n \t\tOPT_BOOLEAN('f', NULL, &force, \"force move/rename even if target exists\"),\n \t\tOPT_BOOLEAN('k', NULL, &ignore_errors, \"skip move/rename errors\"),\n \t\tOPT_END(),\n@@ -220,15 +309,20 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \t\tenum update_mode mode = modes[i];\n \t\tif (show_only || verbose)\n \t\t\tprintf(\"Renaming %s to %s\\n\", src, dst);\n-\t\tif (!show_only && mode != INDEX &&\n-\t\t\t\trename(src, dst) < 0 && !ignore_errors)\n-\t\t\tdie (\"renaming %s failed: %s\", src, strerror(errno));\n+\t\tif (!show_only && mode != INDEX)\n+\t\t{\n+\t\t\tif(!copy && rename(src, dst) < 0 && !ignore_errors)\n+\t\t\t\tdie (\"renaming %s failed: %s\", src, strerror(errno));\n+\t\t\telse if (copytree(src, dst) < 0 && !ignore_errors)\n+\t\t\t\tdie (\"copying %s failed: %s\", src, strerror(errno));\n+\t\t}\n \n \t\tif (mode == WORKING_DIRECTORY)\n \t\t\tcontinue;\n \n \t\tif (cache_name_pos(src, strlen(src)) >= 0) {\n-\t\t\tpath_list_insert(src, &deleted);\n+\t\t\tif(!copy)\n+\t\t\t\tpath_list_insert(src, &deleted);\n \n \t\t\t/* destination can be a directory with 1 file inside */\n \t\t\tif (path_list_has_path(&overwritten, dst))\ndiff --git a/builtin.h b/builtin.h\nindex cb675c4..e1a85fe 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -28,6 +28,7 @@ extern int cmd_clean(int argc, const char **argv, const char *prefix);\n extern int cmd_commit(int argc, const char **argv, const char *prefix);\n extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_count_objects(int argc, const char **argv, const char *prefix);\n+extern int cmd_cp(int argc, const char **argv, const char *prefix);\n extern int cmd_describe(int argc, const char **argv, const char *prefix);\n extern int cmd_diff_files(int argc, const char **argv, const char *prefix);\n extern int cmd_diff_index(int argc, const char **argv, const char *prefix);\ndiff --git a/command-list.txt b/command-list.txt\nindex 3583a33..ddfcdaf 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -24,6 +24,7 @@ git-commit                              mainporcelain common\n git-commit-tree                         plumbingmanipulators\n git-config                              ancillarymanipulators\n git-count-objects                       ancillaryinterrogators\n+git-cp                                  mainporcelain common\n git-cvsexportcommit                     foreignscminterface\n git-cvsimport                           foreignscminterface\n git-cvsserver                           foreignscminterface\ndiff --git a/git.c b/git.c\nindex 15fec89..f6a104b 100644\n--- a/git.c\n+++ b/git.c\n@@ -298,6 +298,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"commit-tree\", cmd_commit_tree, RUN_SETUP },\n \t\t{ \"config\", cmd_config },\n \t\t{ \"count-objects\", cmd_count_objects, RUN_SETUP },\n+\t\t{ \"cp\", cmd_cp, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"describe\", cmd_describe, RUN_SETUP },\n \t\t{ \"diff\", cmd_diff },\n \t\t{ \"diff-files\", cmd_diff_files },\ndiff --git a/t/t7006-cp.sh b/t/t7006-cp.sh\nnew file mode 100755\nindex 0000000..217072c\n--- /dev/null\n+++ b/t/t7006-cp.sh\n@@ -0,0 +1,75 @@\n+#!/bin/sh\n+\n+test_description='git cp in subdirs'\n+. ./test-lib.sh\n+\n+test_expect_success \\\n+    'prepare reference tree' \\\n+    'mkdir path0 path1 &&\n+     cp ../../COPYING path0/COPYING &&\n+     git add path0/COPYING &&\n+     git-commit -m add -a'\n+\n+test_expect_success \\\n+    'copying the file out of subdirectory' \\\n+    'cd path0 && git cp COPYING ../path1/COPYING'\n+\n+# in path0 currently\n+test_expect_success \\\n+    'commiting the change' \\\n+    'cd .. && git-commit -m copy-out -a'\n+\n+test_expect_success \\\n+    'checking the commit' \\\n+    'git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \\\n+    grep \"^C100..*path0/COPYING..*path1/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+    'copying whole subdirectory' \\\n+    'git cp path0 path2'\n+\n+test_expect_success \\\n+    'commiting the change' \\\n+    'git-commit -m dir-copy -a'\n+\n+test_expect_success \\\n+    'checking the commit' \\\n+    'git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \\\n+     grep \"^C100..*path0/COPYING..*path2/COPYING\" &&\n+     git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \\\n+     grep \"^C100..*path0/README..*path2/README\"'\n+\n+test_expect_success \\\n+    'succeed when source is a prefix of destination' \\\n+    'git cp path2/COPYING path2/COPYING-copied'\n+\n+test_expect_success \\\n+    'copying whole subdirectory into subdirectory' \\\n+    'git cp path2 path1'\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 -C --find-copies-harder --name-status  HEAD^ HEAD | \\\n+     grep \"^C100..*path0/COPYING..*path1/path2/COPYING\" &&\n+     git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \\\n+     grep \"^C100..*path0/README..*path1/path2/README\"'\n+\n+test_expect_success \\\n+    'do not copy directory over existing directory' \\\n+    'rm -rf path0 && mkdir path0 && mkdir path0/path2 && ! git cp path2 path0'\n+\n+test_expect_success \\\n+    'copy \".\"' \\\n+    'rm -rf path2 && git cp path1/path2/ .'\n+\n+test_done\n-- \n1.5.4\n"},{"id":"68141","messageId":"alpine.LSU.1.00.0802100125510.11591@racer.site","threadId":"11858","inReplyTo":"20080210011203.GL25954@genesis.frugalware.org","subject":"Re: [RFC/PATCH] Implement git-cp.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-10T01:26:44Z","receivedAt":"2008-02-10T01:26:44Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 10 Feb 2008, Miklos Vajna wrote:\n\n\n>  builtin-cp.c                 |   24 ++++++++++\n>  builtin-mv.c                 |  104 ++++++++++++++++++++++++++++++++++++++++--\n\nIf you touch builtin-mv.c already, why not just move cmd_cp() in there?  \nIt's not like it would be the first cmd_*() function living in the same \nfile as other cmd_*() functions.\n\nCiao,\nDscho\n"},{"id":"68181","messageId":"7vzlu9uu7c.fsf@gitster.siamese.dyndns.org","threadId":"11858","inReplyTo":"alpine.LSU.1.00.0802100125510.11591@racer.site","subject":"Re: [RFC/PATCH] Implement git-cp.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-10T07:49:43Z","receivedAt":"2008-02-10T07:49:43Z","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> If you touch builtin-mv.c already, why not just move cmd_cp() in there?  \n> It's not like it would be the first cmd_*() function living in the same \n> file as other cmd_*() functions.\n\nWhy do we even want \"git-cp\", especially when git-mv and git-rm\nare already pretty much redundant commands?\n\nEspecially, why do we even encourage copy-and-paste?\n"},{"id":"68203","messageId":"alpine.LSU.1.00.0802101230160.11591@racer.site","threadId":"11858","inReplyTo":"7vzlu9uu7c.fsf@gitster.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Implement git-cp.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-10T12:33:33Z","receivedAt":"2008-02-10T12:33:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 9 Feb 2008, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > If you touch builtin-mv.c already, why not just move cmd_cp() in \n> > there?  It's not like it would be the first cmd_*() function living in \n> > the same file as other cmd_*() functions.\n> \n> Why do we even want \"git-cp\", especially when git-mv and git-rm are \n> already pretty much redundant commands?\n> \n> Especially, why do we even encourage copy-and-paste?\n\nHeh.  I quote:\n\n\tIf you want to shoot yourself in the foot Git will point you to \n\tthe gun rack and show you how to load the bullets. (Masukomi)\n\nBut yeah, I did not really think about it.  I had the impression \noriginally that it would make sense to copy a few tracked files around, in \norder to modify them.\n\nThink \"kernel module\".  You'd not start from scratch, but find a similar \none, and then change the heck out of it.\n\nBut your comment made me think again: It's better to \"cp\" those \nfiles/directories, and then \"git add\" the relevant ones.  It even avoids \nstupid mistakes where you commit something you \"cp\"ed, but did not need.\n\nCiao,\nDscho\n"},{"id":"68204","messageId":"20080210124252.GN25954@genesis.frugalware.org","threadId":"11858","inReplyTo":"alpine.LSU.1.00.0802101230160.11591@racer.site","subject":"Re: [RFC/PATCH] Implement git-cp.","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-02-10T12:42:52Z","receivedAt":"2008-02-10T12:42:52Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Sun, Feb 10, 2008 at 12:33:33PM +0000, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> But your comment made me think again: It's better to \"cp\" those \n> files/directories, and then \"git add\" the relevant ones.  It even avoids \n> stupid mistakes where you commit something you \"cp\"ed, but did not need.\n\nthat's exactly why i think git-cp makes sense. git cp copies the\ndirectory tree and adds the tracked files to the index. if you use cp -R\nto copy the directory tree then git add, you can easily add untracked\nfiles, while git cp avoids this.\n\nif you count gitignore, then it's like: why having git commit -a if we\nhave git add -u and git commit?\n\n(if i'm right you mean: use cp -R and git add, instead of git cp)\n\nthanks\n- VMiklos\n"},{"id":"68210","messageId":"200802101429.04765.robin.rosenberg.lists@dewire.com","threadId":"11858","inReplyTo":"7vzlu9uu7c.fsf@gitster.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Implement git-cp.","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2008-02-10T13:29:04Z","receivedAt":"2008-02-10T13:29:04Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"söndagen den 10 februari 2008 skrev Junio C Hamano:\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > If you touch builtin-mv.c already, why not just move cmd_cp() in there?  \n> > It's not like it would be the first cmd_*() function living in the same \n> > file as other cmd_*() functions.\n> \n> Why do we even want \"git-cp\", especially when git-mv and git-rm\n> are already pretty much redundant commands?\n> \n> Especially, why do we even encourage copy-and-paste?\n\nWe do not. I've seen too much damage being done through copy/paste. A\nmuch better (safer) way to copy code is to re-type it. I've asked more\nthan once, when asked to help debug a piece of code: \"Did you copy this \ncode?\".\n\nFor this reason I have a vision of a \"plugin\" for Eclipse that disables paste\nif the clipboard contains copied code. Cut and paste would be ok.\n\n-- robin\n"},{"id":"68243","messageId":"20080210182419.GP25954@genesis.frugalware.org","threadId":"11858","inReplyTo":"alpine.LSU.1.00.0802100125510.11591@racer.site","subject":"[PATCH] Implement git-cp.","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-02-10T18:24:19Z","receivedAt":"2008-02-10T18:24:19Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"Actually it adds a -c option to git-mv to copy instead of move and add a\nbuiltin alias git-cp for 'git mv -c'.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n\nOn Sun, Feb 10, 2008 at 01:26:44AM +0000, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> >  builtin-cp.c                 |   24 ++++++++++\n> >  builtin-mv.c                 |  104 ++++++++++++++++++++++++++++++++++++++++--\n>\n> If you touch builtin-mv.c already, why not just move cmd_cp() in there?\n> It's not like it would be the first cmd_*() function living in the same\n> file as other cmd_*() functions.\n\nannotate vs blame was like this. but here is it, without builtin-cp.o.\n\n .gitignore                   |    1 +\n Documentation/git-cp.txt     |   42 +++++++++++++++\n Documentation/git-mv.txt     |   12 +---\n Documentation/mv-options.txt |    9 +++\n builtin-mv.c                 |  121 ++++++++++++++++++++++++++++++++++++++++--\n builtin.h                    |    1 +\n command-list.txt             |    1 +\n git.c                        |    1 +\n t/t7006-cp.sh                |   75 ++++++++++++++++++++++++++\n 9 files changed, 249 insertions(+), 14 deletions(-)\n create mode 100644 Documentation/git-cp.txt\n create mode 100644 Documentation/mv-options.txt\n create mode 100755 t/t7006-cp.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex 7f8421d..3d1dcdd 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -26,6 +26,7 @@ git-commit\n git-commit-tree\n git-config\n git-count-objects\n+git-cp\n git-cvsexportcommit\n git-cvsimport\n git-cvsserver\ndiff --git a/Documentation/git-cp.txt b/Documentation/git-cp.txt\nnew file mode 100644\nindex 0000000..3109d04\n--- /dev/null\n+++ b/Documentation/git-cp.txt\n@@ -0,0 +1,42 @@\n+git-cp(1)\n+=========\n+\n+NAME\n+----\n+git-cp - Copy a file, a directory, or a symlink\n+\n+SYNOPSIS\n+--------\n+'git-cp' <options>... <args>...\n+\n+DESCRIPTION\n+-----------\n+This script is used to copy a file, directory or symlink.\n+\n+ git-cp [-f] [-n] <source> <destination>\n+ git-cp [-f] [-n] [-k] <source> ... <destination directory>\n+\n+In the first form, it copies <source>, which must exist and be either\n+a file, symlink or directory, to <destination>.\n+In the second form, the last argument has to be an existing\n+directory; the given sources will be copied into this directory.\n+\n+The index is updated after successful completion, but the change must still be\n+committed.\n+\n+\n+OPTIONS\n+-------\n+include::mv-options.txt[]\n+\n+SEE ALSO\n+--------\n+linkgit:git-mv[1]\n+\n+AUTHOR\n+------\n+Written by Miklos Vajna <vmiklos@frugalware.org>.\n+\n+GIT\n+---\n+Part of the linkgit:git[7] suite\ndiff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt\nindex bff3fbe..45d8255 100644\n--- a/Documentation/git-mv.txt\n+++ b/Documentation/git-mv.txt\n@@ -27,15 +27,9 @@ committed.\n \n OPTIONS\n -------\n--f::\n-\tForce renaming or moving of a file even if the target exists\n--k::\n-        Skip move or rename actions which would lead to an error\n-\tcondition. An error happens when a source is neither existing nor\n-        controlled by GIT, or when it would overwrite an existing\n-        file unless '-f' is given.\n--n, \\--dry-run::\n-\tDo nothing; only show what would happen\n+include::mv-options.txt[]\n+-c::\n+\tCopy instead of move.\n \n \n Author\ndiff --git a/Documentation/mv-options.txt b/Documentation/mv-options.txt\nnew file mode 100644\nindex 0000000..ad78d1e\n--- /dev/null\n+++ b/Documentation/mv-options.txt\n@@ -0,0 +1,9 @@\n+-f::\n+\tForce renaming or moving of a file even if the target exists\n+-k::\n+        Skip move or rename actions which would lead to an error\n+\tcondition. An error happens when a source is neither existing nor\n+        controlled by GIT, or when it would overwrite an existing\n+        file unless '-f' is given.\n+-n, \\--dry-run::\n+\tDo nothing; only show what would happen\ndiff --git a/builtin-mv.c b/builtin-mv.c\nindex 990e213..08a5526 100644\n--- a/builtin-mv.c\n+++ b/builtin-mv.c\n@@ -60,14 +60,103 @@ static const char *add_slash(const char *path)\n \treturn path;\n }\n \n+static int copyfile(const char *src_name, const char *dst_name)\n+{\n+\tint ifd;\n+\tint ofd;\n+\tstruct stat sb;\n+\tint cnt;\n+\tchar buf[1024];\n+\n+\tstat(src_name, &sb);\n+\tif ((ifd = open (src_name, O_RDONLY)) < 0)\n+\t\tdie(\"could not open file %s for reading: %s\", src_name, strerror(errno));\n+\tif ((ofd =\n+\t     open (dst_name, O_WRONLY | O_CREAT | O_TRUNC, 0)) < 0\n+\t    || chmod (dst_name, sb.st_mode & 07777))\n+\t\tdie(\"could not open file %s for writing: %s\", dst_name, strerror(errno));\n+\twhile ((cnt = read (ifd, buf, sizeof buf)) > 0) {\n+\t\tif (write (ofd, buf, cnt) != cnt)\n+\t\t\tdie(\"could not write to file %s\", dst_name);\n+\t}\n+\tclose (ifd);\n+\tclose (ofd);\n+\treturn 0;\n+}\n+\n+static int copytree (const char *src_root, const char *dst_root)\n+{\n+\tchar src_name[1024];\n+\tchar dst_name[1024];\n+\tint err = 0;\n+\tstruct dirent *ent;\n+\tstruct stat sb;\n+\tDIR *dir;\n+\n+\tlstat(src_root, &sb);\n+\n+\tif (!(dir = opendir (src_root)))\n+\t\treturn copyfile(src_root, dst_root);\n+\telse\n+\t\tmkdir(dst_root, sb.st_mode);\n+\n+\twhile ((ent = readdir (dir))) {\n+\n+\t\tif (strcmp (ent->d_name, \".\") == 0 ||\n+\t\t    strcmp (ent->d_name, \"..\") == 0)\n+\t\t\tcontinue;\n+\n+\t\tsnprintf (src_name, sizeof src_name, \"%s/%s\", src_root,\n+\t\t\t  ent->d_name);\n+\n+\t\tsnprintf (dst_name, sizeof dst_name, \"%s/%s\", dst_root,\n+\t\t\t  ent->d_name);\n+\n+\t\tif (lstat (src_name, &sb) < 0)\n+\t\t\tcontinue;\n+\n+\t\tif (S_ISDIR (sb.st_mode)) {\n+\n+\t\t\tif (mkdir (dst_name, sb.st_mode)\n+\t\t\t    || chmod (dst_name, sb.st_mode & 07777)\n+\t\t\t    || copytree (src_name, dst_name)) {\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (S_ISLNK (sb.st_mode)) {\n+\t\t\tchar oldlink[1024];\n+\t\t\tchar dummy[1024];\n+\t\t\tint len;\n+\n+\t\t\tif ((len =\n+\t\t\t     readlink (src_name, oldlink,\n+\t\t\t\t       sizeof (oldlink) - 1)) < 0)\n+\t\t\t\tdie(\"could not read symlink %s: %s\", src_name, strerror(errno));\n+\t\t\toldlink[len] = '\\0'; /* readlink() does not NULL-terminate */\n+\t\t\tif (symlink (oldlink, dst_name))\n+\t\t\t\tdie(\"could not create symlink %s: %s\", dst_name, strerror(errno));\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (copyfile(src_name, dst_name) == -1)\n+\t\t\tbreak;\n+\t}\n+\tclosedir (dir);\n+\n+\treturn 0;\n+}\n+\n static struct lock_file lock_file;\n \n int cmd_mv(int argc, const char **argv, const char *prefix)\n {\n \tint i, newfd;\n-\tint verbose = 0, show_only = 0, force = 0, ignore_errors = 0;\n+\tint verbose = 0, show_only = 0, copy = 0, force = 0, ignore_errors = 0;\n \tstruct option builtin_mv_options[] = {\n \t\tOPT__DRY_RUN(&show_only),\n+\t\tOPT_BOOLEAN('c', NULL, &copy, \"copy instead of move/rename\"),\n \t\tOPT_BOOLEAN('f', NULL, &force, \"force move/rename even if target exists\"),\n \t\tOPT_BOOLEAN('k', NULL, &ignore_errors, \"skip move/rename errors\"),\n \t\tOPT_END(),\n@@ -220,15 +309,20 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \t\tenum update_mode mode = modes[i];\n \t\tif (show_only || verbose)\n \t\t\tprintf(\"Renaming %s to %s\\n\", src, dst);\n-\t\tif (!show_only && mode != INDEX &&\n-\t\t\t\trename(src, dst) < 0 && !ignore_errors)\n-\t\t\tdie (\"renaming %s failed: %s\", src, strerror(errno));\n+\t\tif (!show_only && mode != INDEX)\n+\t\t{\n+\t\t\tif(!copy && rename(src, dst) < 0 && !ignore_errors)\n+\t\t\t\tdie (\"renaming %s failed: %s\", src, strerror(errno));\n+\t\t\telse if (copytree(src, dst) < 0 && !ignore_errors)\n+\t\t\t\tdie (\"copying %s failed: %s\", src, strerror(errno));\n+\t\t}\n \n \t\tif (mode == WORKING_DIRECTORY)\n \t\t\tcontinue;\n \n \t\tif (cache_name_pos(src, strlen(src)) >= 0) {\n-\t\t\tpath_list_insert(src, &deleted);\n+\t\t\tif(!copy)\n+\t\t\t\tpath_list_insert(src, &deleted);\n \n \t\t\t/* destination can be a directory with 1 file inside */\n \t\t\tif (path_list_has_path(&overwritten, dst))\n@@ -271,3 +365,20 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \n \treturn 0;\n }\n+\n+int cmd_cp(int argc, const char **argv, const char *prefix)\n+{\n+\tconst char **nargv;\n+\tint i;\n+\tnargv = xmalloc(sizeof(char *) * (argc + 2));\n+\n+\tnargv[0] = \"mv\";\n+\tnargv[1] = \"-c\";\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tnargv[i+1] = argv[i];\n+\t}\n+\tnargv[argc + 1] = NULL;\n+\n+\treturn cmd_mv(argc + 1, nargv, prefix);\n+}\ndiff --git a/builtin.h b/builtin.h\nindex cb675c4..e1a85fe 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -28,6 +28,7 @@ extern int cmd_clean(int argc, const char **argv, const char *prefix);\n extern int cmd_commit(int argc, const char **argv, const char *prefix);\n extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_count_objects(int argc, const char **argv, const char *prefix);\n+extern int cmd_cp(int argc, const char **argv, const char *prefix);\n extern int cmd_describe(int argc, const char **argv, const char *prefix);\n extern int cmd_diff_files(int argc, const char **argv, const char *prefix);\n extern int cmd_diff_index(int argc, const char **argv, const char *prefix);\ndiff --git a/command-list.txt b/command-list.txt\nindex 3583a33..ddfcdaf 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -24,6 +24,7 @@ git-commit                              mainporcelain common\n git-commit-tree                         plumbingmanipulators\n git-config                              ancillarymanipulators\n git-count-objects                       ancillaryinterrogators\n+git-cp                                  mainporcelain common\n git-cvsexportcommit                     foreignscminterface\n git-cvsimport                           foreignscminterface\n git-cvsserver                           foreignscminterface\ndiff --git a/git.c b/git.c\nindex 15fec89..f6a104b 100644\n--- a/git.c\n+++ b/git.c\n@@ -298,6 +298,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"commit-tree\", cmd_commit_tree, RUN_SETUP },\n \t\t{ \"config\", cmd_config },\n \t\t{ \"count-objects\", cmd_count_objects, RUN_SETUP },\n+\t\t{ \"cp\", cmd_cp, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"describe\", cmd_describe, RUN_SETUP },\n \t\t{ \"diff\", cmd_diff },\n \t\t{ \"diff-files\", cmd_diff_files },\ndiff --git a/t/t7006-cp.sh b/t/t7006-cp.sh\nnew file mode 100755\nindex 0000000..217072c\n--- /dev/null\n+++ b/t/t7006-cp.sh\n@@ -0,0 +1,75 @@\n+#!/bin/sh\n+\n+test_description='git cp in subdirs'\n+. ./test-lib.sh\n+\n+test_expect_success \\\n+    'prepare reference tree' \\\n+    'mkdir path0 path1 &&\n+     cp ../../COPYING path0/COPYING &&\n+     git add path0/COPYING &&\n+     git-commit -m add -a'\n+\n+test_expect_success \\\n+    'copying the file out of subdirectory' \\\n+    'cd path0 && git cp COPYING ../path1/COPYING'\n+\n+# in path0 currently\n+test_expect_success \\\n+    'commiting the change' \\\n+    'cd .. && git-commit -m copy-out -a'\n+\n+test_expect_success \\\n+    'checking the commit' \\\n+    'git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \\\n+    grep \"^C100..*path0/COPYING..*path1/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+    'copying whole subdirectory' \\\n+    'git cp path0 path2'\n+\n+test_expect_success \\\n+    'commiting the change' \\\n+    'git-commit -m dir-copy -a'\n+\n+test_expect_success \\\n+    'checking the commit' \\\n+    'git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \\\n+     grep \"^C100..*path0/COPYING..*path2/COPYING\" &&\n+     git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \\\n+     grep \"^C100..*path0/README..*path2/README\"'\n+\n+test_expect_success \\\n+    'succeed when source is a prefix of destination' \\\n+    'git cp path2/COPYING path2/COPYING-copied'\n+\n+test_expect_success \\\n+    'copying whole subdirectory into subdirectory' \\\n+    'git cp path2 path1'\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 -C --find-copies-harder --name-status  HEAD^ HEAD | \\\n+     grep \"^C100..*path0/COPYING..*path1/path2/COPYING\" &&\n+     git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \\\n+     grep \"^C100..*path0/README..*path1/path2/README\"'\n+\n+test_expect_success \\\n+    'do not copy directory over existing directory' \\\n+    'rm -rf path0 && mkdir path0 && mkdir path0/path2 && ! git cp path2 path0'\n+\n+test_expect_success \\\n+    'copy \".\"' \\\n+    'rm -rf path2 && git cp path1/path2/ .'\n+\n+test_done\n-- \n1.5.4\n"},{"id":"68306","messageId":"200802110230.06808.jnareb@gmail.com","threadId":"11858","inReplyTo":"7vzlu9uu7c.fsf@gitster.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Implement git-cp.","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2008-02-11T01:30:06Z","receivedAt":"2008-02-11T01:30:06Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > If you touch builtin-mv.c already, why not just move cmd_cp() in there?  \n> > It's not like it would be the first cmd_*() function living in the same \n> > file as other cmd_*() functions.\n> \n> Why do we even want \"git-cp\", especially when git-mv and git-rm\n> are already pretty much redundant commands?\n\ngit-rm is not redundant: without it how we could easily un-add\na file (without resorting to porcelain or tricks). I think it\nis also safer that ordinary rm (check for being up-to-date, i.e.\nfor modified files).\n\nThe above also reflects git-mv, as half of it is git-rm (although\nwithout some issues). Also git-mv could skip over non git-controlled\nfiles.\n\n> Especially, why do we even encourage copy-and-paste?\n\nBut git-cp would be IMHO a convenience only. The half of pros for\ngit-mv applies to this too; unfortunately the second part of pros\nfor git-mv does not apply for git-cp.\n\nWould git-cp encourage copy'n'paste programming? I'm not sure...\n\n-- \nJakub Narebski\nPoland\n"},{"id":"68362","messageId":"vpq1w7jzthu.fsf@bauges.imag.fr","threadId":"11858","inReplyTo":"7vzlu9uu7c.fsf@gitster.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Implement git-cp.","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2008-02-11T10:18:21Z","receivedAt":"2008-02-11T10:18:21Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>\n>> If you touch builtin-mv.c already, why not just move cmd_cp() in there?  \n>> It's not like it would be the first cmd_*() function living in the same \n>> file as other cmd_*() functions.\n>\n> Why do we even want \"git-cp\", especially when git-mv and git-rm\n> are already pretty much redundant commands?\n>\n> Especially, why do we even encourage copy-and-paste?\n\nI rarely (let's say: never ;-) ) copy-and-paste a C source file, but\nthere are tons of cases wher I start a file by copying another\n(typically, a 10 lines long Makefile containing macro definitions and\nan \"include\" statement, to be copied in each subdirectory and adapted\nto reflect the source file name, or starting a .txt file based on a\nskeleton).\n\nI can very well live without a \"git cp\" command, but I'd use it if it\nwas available.\n\n-- \nMatthieu\n"},{"id":"68390","messageId":"20080211134312.GW25954@genesis.frugalware.org","threadId":"11858","inReplyTo":"vpq1w7jzthu.fsf@bauges.imag.fr","subject":"Re: [RFC/PATCH] Implement git-cp.","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-02-11T13:43:12Z","receivedAt":"2008-02-11T13:43:12Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Mon, Feb 11, 2008 at 11:18:21AM +0100, Matthieu Moy <Matthieu.Moy@imag.fr> wrote:\n> I rarely (let's say: never ;-) ) copy-and-paste a C source file, but\n> I can very well live without a \"git cp\" command, but I'd use it if it\n> was available.\n\nsame here. i can live without git add -u and git cp but they are both\nquite handy and i'm happy that git add -u is in git.git. i think other\npeople would use it sometimes as well.\n\nthanks,\n- VMiklos\n"}]}