All of lore.kernel.org
 help / color / mirror / Atom feed
* What about git cp ?
@ 2008-02-03 18:23 Francis Moreau
  2008-02-03 18:46 ` Matthieu Moy
  2008-02-03 18:55 ` What about git cp ? Remi Vanicat
  0 siblings, 2 replies; 14+ messages in thread
From: Francis Moreau @ 2008-02-03 18:23 UTC (permalink / raw)
  To: git

Hello,

I'm looking for a something which could be done by a git-cp command.

I'd like to copy a file with its history to a new  file but want to keep the old
one,

Thanks
-- 
Francis

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: What about git cp ?
  2008-02-03 18:23 What about git cp ? Francis Moreau
@ 2008-02-03 18:46 ` Matthieu Moy
  2008-02-03 19:07   ` Jakub Narebski
  2008-02-03 18:55 ` What about git cp ? Remi Vanicat
  1 sibling, 1 reply; 14+ messages in thread
From: Matthieu Moy @ 2008-02-03 18:46 UTC (permalink / raw)
  To: Francis Moreau; +Cc: git

"Francis Moreau" <francis.moro@gmail.com> writes:

> Hello,
>
> I'm looking for a something which could be done by a git-cp command.
>
> I'd like to copy a file with its history to a new  file but want to keep the old
> one,

Git doesn't _record_ copies and renames, but detects them
after-the-fact (either by default, or explicitly like "git blame -C"
or so).

So, just "cp + git add" and you're done. That's what a "git cp"
command would do if it existed.

-- 
Matthieu

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: What about git cp ?
  2008-02-03 18:23 What about git cp ? Francis Moreau
  2008-02-03 18:46 ` Matthieu Moy
@ 2008-02-03 18:55 ` Remi Vanicat
  1 sibling, 0 replies; 14+ messages in thread
From: Remi Vanicat @ 2008-02-03 18:55 UTC (permalink / raw)
  To: Francis Moreau; +Cc: git

"Francis Moreau" <francis.moro@gmail.com> writes:

> Hello,
>
> I'm looking for a something which could be done by a git-cp command.
>
> I'd like to copy a file with its history to a new  file but want to
> keep the old one,

Git don't attach history to a file, but to a content. Just do a:
cp foo bar
git add bar
git commit -m "copying foo to bar"

And it will be done, and when needed, git will be able to know that
this was a copy.

-- 
Rémi Vanicat

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: What about git cp ?
  2008-02-03 18:46 ` Matthieu Moy
@ 2008-02-03 19:07   ` Jakub Narebski
  2008-02-10  1:12     ` [RFC/PATCH] Implement git-cp Miklos Vajna
  0 siblings, 1 reply; 14+ messages in thread
From: Jakub Narebski @ 2008-02-03 19:07 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Francis Moreau, git

Matthieu Moy <Matthieu.Moy@imag.fr> writes:

> "Francis Moreau" <francis.moro@gmail.com> writes:
> 
> > Hello,
> >
> > I'm looking for a something which could be done by a git-cp
> > command.
> >
> > I'd like to copy a file with its history to a new file but want to
> > keep the old one,
> 
> Git doesn't _record_ copies and renames, but detects them
> after-the-fact (either by default, or explicitly like "git blame -C"
> or so).
> 
> So, just "cp + git add" and you're done. That's what a "git cp"
> command would do if it existed.

You can just put git-cp shell script doing this in PATH (or
GIT_EXEC_PATH if they are different) for "git cp" to just work.
Or you can write builtin-cp.c, following builtin-mv.c

-- 
Jakub Narebski
Poland
ShadeHawk on #git

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [RFC/PATCH] Implement git-cp.
  2008-02-03 19:07   ` Jakub Narebski
@ 2008-02-10  1:12     ` Miklos Vajna
  2008-02-10  1:26       ` Johannes Schindelin
  0 siblings, 1 reply; 14+ messages in thread
From: Miklos Vajna @ 2008-02-10  1:12 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jakub Narebski, Matthieu Moy, Francis Moreau, git

Actually it adds a -c option to git-mv to copy instead of move and add a
builtin alias git-cp for 'git mv -c'.

Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
---

On Sun, Feb 03, 2008 at 11:07:10AM -0800, Jakub Narebski <jnareb@gmail.com> wrote:
> You can just put git-cp shell script doing this in PATH (or
> GIT_EXEC_PATH if they are different) for "git cp" to just work.
> Or you can write builtin-cp.c, following builtin-mv.c

something like this?

 .gitignore                   |    1 +
 Documentation/git-cp.txt     |   42 +++++++++++++++++
 Documentation/git-mv.txt     |   12 +----
 Documentation/mv-options.txt |    9 ++++
 Makefile                     |    1 +
 builtin-cp.c                 |   24 ++++++++++
 builtin-mv.c                 |  104 ++++++++++++++++++++++++++++++++++++++++--
 builtin.h                    |    1 +
 command-list.txt             |    1 +
 git.c                        |    1 +
 t/t7006-cp.sh                |   75 ++++++++++++++++++++++++++++++
 11 files changed, 257 insertions(+), 14 deletions(-)
 create mode 100644 Documentation/git-cp.txt
 create mode 100644 Documentation/mv-options.txt
 create mode 100644 builtin-cp.c
 create mode 100755 t/t7006-cp.sh

diff --git a/.gitignore b/.gitignore
index 7f8421d..3d1dcdd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,7 @@ git-commit
 git-commit-tree
 git-config
 git-count-objects
+git-cp
 git-cvsexportcommit
 git-cvsimport
 git-cvsserver
diff --git a/Documentation/git-cp.txt b/Documentation/git-cp.txt
new file mode 100644
index 0000000..3109d04
--- /dev/null
+++ b/Documentation/git-cp.txt
@@ -0,0 +1,42 @@
+git-cp(1)
+=========
+
+NAME
+----
+git-cp - Copy a file, a directory, or a symlink
+
+SYNOPSIS
+--------
+'git-cp' <options>... <args>...
+
+DESCRIPTION
+-----------
+This script is used to copy a file, directory or symlink.
+
+ git-cp [-f] [-n] <source> <destination>
+ git-cp [-f] [-n] [-k] <source> ... <destination directory>
+
+In the first form, it copies <source>, which must exist and be either
+a file, symlink or directory, to <destination>.
+In the second form, the last argument has to be an existing
+directory; the given sources will be copied into this directory.
+
+The index is updated after successful completion, but the change must still be
+committed.
+
+
+OPTIONS
+-------
+include::mv-options.txt[]
+
+SEE ALSO
+--------
+linkgit:git-mv[1]
+
+AUTHOR
+------
+Written by Miklos Vajna <vmiklos@frugalware.org>.
+
+GIT
+---
+Part of the linkgit:git[7] suite
diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt
index bff3fbe..45d8255 100644
--- a/Documentation/git-mv.txt
+++ b/Documentation/git-mv.txt
@@ -27,15 +27,9 @@ committed.
 
 OPTIONS
 -------
--f::
-	Force renaming or moving of a file even if the target exists
--k::
-        Skip move or rename actions which would lead to an error
-	condition. An error happens when a source is neither existing nor
-        controlled by GIT, or when it would overwrite an existing
-        file unless '-f' is given.
--n, \--dry-run::
-	Do nothing; only show what would happen
+include::mv-options.txt[]
+-c::
+	Copy instead of move.
 
 
 Author
diff --git a/Documentation/mv-options.txt b/Documentation/mv-options.txt
new file mode 100644
index 0000000..ad78d1e
--- /dev/null
+++ b/Documentation/mv-options.txt
@@ -0,0 +1,9 @@
+-f::
+	Force renaming or moving of a file even if the target exists
+-k::
+        Skip move or rename actions which would lead to an error
+	condition. An error happens when a source is neither existing nor
+        controlled by GIT, or when it would overwrite an existing
+        file unless '-f' is given.
+-n, \--dry-run::
+	Do nothing; only show what would happen
diff --git a/Makefile b/Makefile
index 92341c4..0dd8e02 100644
--- a/Makefile
+++ b/Makefile
@@ -338,6 +338,7 @@ BUILTIN_OBJS = \
 	builtin-commit.o \
 	builtin-commit-tree.o \
 	builtin-count-objects.o \
+	builtin-cp.o \
 	builtin-describe.o \
 	builtin-diff.o \
 	builtin-diff-files.o \
diff --git a/builtin-cp.c b/builtin-cp.c
new file mode 100644
index 0000000..f66daba
--- /dev/null
+++ b/builtin-cp.c
@@ -0,0 +1,24 @@
+/*
+ * "git cp" builtin alias
+ *
+ * Copyright (C) 2008 Miklos Vajna
+ */
+#include "git-compat-util.h"
+#include "builtin.h"
+
+int cmd_cp(int argc, const char **argv, const char *prefix)
+{
+	const char **nargv;
+	int i;
+	nargv = xmalloc(sizeof(char *) * (argc + 2));
+
+	nargv[0] = "mv";
+	nargv[1] = "-c";
+
+	for (i = 1; i < argc; i++) {
+		nargv[i+1] = argv[i];
+	}
+	nargv[argc + 1] = NULL;
+
+	return cmd_mv(argc + 1, nargv, prefix);
+}
diff --git a/builtin-mv.c b/builtin-mv.c
index 990e213..8c069a5 100644
--- a/builtin-mv.c
+++ b/builtin-mv.c
@@ -60,14 +60,103 @@ static const char *add_slash(const char *path)
 	return path;
 }
 
+static int copyfile(const char *src_name, const char *dst_name)
+{
+	int ifd;
+	int ofd;
+	struct stat sb;
+	int cnt;
+	char buf[1024];
+
+	stat(src_name, &sb);
+	if ((ifd = open (src_name, O_RDONLY)) < 0)
+		die("could not open file %s for reading: %s", src_name, strerror(errno));
+	if ((ofd =
+	     open (dst_name, O_WRONLY | O_CREAT | O_TRUNC, 0)) < 0
+	    || chmod (dst_name, sb.st_mode & 07777))
+		die("could not open file %s for writing: %s", dst_name, strerror(errno));
+	while ((cnt = read (ifd, buf, sizeof buf)) > 0) {
+		if (write (ofd, buf, cnt) != cnt)
+			die("could not write to file %s", dst_name);
+	}
+	close (ifd);
+	close (ofd);
+	return 0;
+}
+
+static int copytree (const char *src_root, const char *dst_root)
+{
+	char src_name[1024];
+	char dst_name[1024];
+	int err = 0;
+	struct dirent *ent;
+	struct stat sb;
+	DIR *dir;
+
+	lstat(src_root, &sb);
+
+	if (!(dir = opendir (src_root)))
+		return copyfile(src_root, dst_root);
+	else
+		mkdir(dst_root, sb.st_mode);
+
+	while ((ent = readdir (dir))) {
+
+		if (strcmp (ent->d_name, ".") == 0 ||
+		    strcmp (ent->d_name, "..") == 0)
+			continue;
+
+		snprintf (src_name, sizeof src_name, "%s/%s", src_root,
+			  ent->d_name);
+
+		snprintf (dst_name, sizeof dst_name, "%s/%s", dst_root,
+			  ent->d_name);
+
+		if (lstat (src_name, &sb) < 0)
+			continue;
+
+		if (S_ISDIR (sb.st_mode)) {
+
+			if (mkdir (dst_name, sb.st_mode)
+			    || chmod (dst_name, sb.st_mode & 07777)
+			    || copytree (src_name, dst_name)) {
+				break;
+			}
+			continue;
+		}
+
+		if (S_ISLNK (sb.st_mode)) {
+			char oldlink[1024];
+			char dummy[1024];
+			int len;
+
+			if ((len =
+			     readlink (src_name, oldlink,
+				       sizeof (oldlink) - 1)) < 0)
+				die("could not read symlink %s: %s", src_name, strerror(errno));
+			oldlink[len] = '\0'; /* readlink() does not NULL-terminate */
+			if (symlink (oldlink, dst_name))
+				die("could not create symlink %s: %s", dst_name, strerror(errno));
+			continue;
+		}
+
+		if (copyfile(src_name, dst_name) == -1)
+			break;
+	}
+	closedir (dir);
+
+	return 0;
+}
+
 static struct lock_file lock_file;
 
 int cmd_mv(int argc, const char **argv, const char *prefix)
 {
 	int i, newfd;
-	int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
+	int verbose = 0, show_only = 0, copy = 0, force = 0, ignore_errors = 0;
 	struct option builtin_mv_options[] = {
 		OPT__DRY_RUN(&show_only),
+		OPT_BOOLEAN('c', NULL, &copy, "copy instead of move/rename"),
 		OPT_BOOLEAN('f', NULL, &force, "force move/rename even if target exists"),
 		OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"),
 		OPT_END(),
@@ -220,15 +309,20 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 		enum update_mode mode = modes[i];
 		if (show_only || verbose)
 			printf("Renaming %s to %s\n", src, dst);
-		if (!show_only && mode != INDEX &&
-				rename(src, dst) < 0 && !ignore_errors)
-			die ("renaming %s failed: %s", src, strerror(errno));
+		if (!show_only && mode != INDEX)
+		{
+			if(!copy && rename(src, dst) < 0 && !ignore_errors)
+				die ("renaming %s failed: %s", src, strerror(errno));
+			else if (copytree(src, dst) < 0 && !ignore_errors)
+				die ("copying %s failed: %s", src, strerror(errno));
+		}
 
 		if (mode == WORKING_DIRECTORY)
 			continue;
 
 		if (cache_name_pos(src, strlen(src)) >= 0) {
-			path_list_insert(src, &deleted);
+			if(!copy)
+				path_list_insert(src, &deleted);
 
 			/* destination can be a directory with 1 file inside */
 			if (path_list_has_path(&overwritten, dst))
diff --git a/builtin.h b/builtin.h
index cb675c4..e1a85fe 100644
--- a/builtin.h
+++ b/builtin.h
@@ -28,6 +28,7 @@ extern int cmd_clean(int argc, const char **argv, const char *prefix);
 extern int cmd_commit(int argc, const char **argv, const char *prefix);
 extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
+extern int cmd_cp(int argc, const char **argv, const char *prefix);
 extern int cmd_describe(int argc, const char **argv, const char *prefix);
 extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
 extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
diff --git a/command-list.txt b/command-list.txt
index 3583a33..ddfcdaf 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -24,6 +24,7 @@ git-commit                              mainporcelain common
 git-commit-tree                         plumbingmanipulators
 git-config                              ancillarymanipulators
 git-count-objects                       ancillaryinterrogators
+git-cp                                  mainporcelain common
 git-cvsexportcommit                     foreignscminterface
 git-cvsimport                           foreignscminterface
 git-cvsserver                           foreignscminterface
diff --git a/git.c b/git.c
index 15fec89..f6a104b 100644
--- a/git.c
+++ b/git.c
@@ -298,6 +298,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "commit-tree", cmd_commit_tree, RUN_SETUP },
 		{ "config", cmd_config },
 		{ "count-objects", cmd_count_objects, RUN_SETUP },
+		{ "cp", cmd_cp, RUN_SETUP | NEED_WORK_TREE },
 		{ "describe", cmd_describe, RUN_SETUP },
 		{ "diff", cmd_diff },
 		{ "diff-files", cmd_diff_files },
diff --git a/t/t7006-cp.sh b/t/t7006-cp.sh
new file mode 100755
index 0000000..217072c
--- /dev/null
+++ b/t/t7006-cp.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+test_description='git cp in subdirs'
+. ./test-lib.sh
+
+test_expect_success \
+    'prepare reference tree' \
+    'mkdir path0 path1 &&
+     cp ../../COPYING path0/COPYING &&
+     git add path0/COPYING &&
+     git-commit -m add -a'
+
+test_expect_success \
+    'copying the file out of subdirectory' \
+    'cd path0 && git cp COPYING ../path1/COPYING'
+
+# in path0 currently
+test_expect_success \
+    'commiting the change' \
+    'cd .. && git-commit -m copy-out -a'
+
+test_expect_success \
+    'checking the commit' \
+    'git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \
+    grep "^C100..*path0/COPYING..*path1/COPYING"'
+
+test_expect_success \
+    'adding another file' \
+    'cp ../../README path0/README &&
+     git add path0/README &&
+     git-commit -m add2 -a'
+
+test_expect_success \
+    'copying whole subdirectory' \
+    'git cp path0 path2'
+
+test_expect_success \
+    'commiting the change' \
+    'git-commit -m dir-copy -a'
+
+test_expect_success \
+    'checking the commit' \
+    'git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \
+     grep "^C100..*path0/COPYING..*path2/COPYING" &&
+     git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \
+     grep "^C100..*path0/README..*path2/README"'
+
+test_expect_success \
+    'succeed when source is a prefix of destination' \
+    'git cp path2/COPYING path2/COPYING-copied'
+
+test_expect_success \
+    'copying whole subdirectory into subdirectory' \
+    'git cp path2 path1'
+
+test_expect_success \
+    'commiting the change' \
+    'git-commit -m dir-move -a'
+
+test_expect_success \
+    'checking the commit' \
+    'git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \
+     grep "^C100..*path0/COPYING..*path1/path2/COPYING" &&
+     git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \
+     grep "^C100..*path0/README..*path1/path2/README"'
+
+test_expect_success \
+    'do not copy directory over existing directory' \
+    'rm -rf path0 && mkdir path0 && mkdir path0/path2 && ! git cp path2 path0'
+
+test_expect_success \
+    'copy "."' \
+    'rm -rf path2 && git cp path1/path2/ .'
+
+test_done
-- 
1.5.4

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [RFC/PATCH] Implement git-cp.
  2008-02-10  1:12     ` [RFC/PATCH] Implement git-cp Miklos Vajna
@ 2008-02-10  1:26       ` Johannes Schindelin
  2008-02-10  7:49         ` Junio C Hamano
  2008-02-10 18:24         ` [PATCH] " Miklos Vajna
  0 siblings, 2 replies; 14+ messages in thread
From: Johannes Schindelin @ 2008-02-10  1:26 UTC (permalink / raw)
  To: Miklos Vajna
  Cc: Junio C Hamano, Jakub Narebski, Matthieu Moy, Francis Moreau, git

Hi,

On Sun, 10 Feb 2008, Miklos Vajna wrote:


>  builtin-cp.c                 |   24 ++++++++++
>  builtin-mv.c                 |  104 ++++++++++++++++++++++++++++++++++++++++--

If you touch builtin-mv.c already, why not just move cmd_cp() in there?  
It's not like it would be the first cmd_*() function living in the same 
file as other cmd_*() functions.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC/PATCH] Implement git-cp.
  2008-02-10  1:26       ` Johannes Schindelin
@ 2008-02-10  7:49         ` Junio C Hamano
  2008-02-10 12:33           ` Johannes Schindelin
                             ` (3 more replies)
  2008-02-10 18:24         ` [PATCH] " Miklos Vajna
  1 sibling, 4 replies; 14+ messages in thread
From: Junio C Hamano @ 2008-02-10  7:49 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Miklos Vajna, Jakub Narebski, Matthieu Moy, Francis Moreau, git

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> If you touch builtin-mv.c already, why not just move cmd_cp() in there?  
> It's not like it would be the first cmd_*() function living in the same 
> file as other cmd_*() functions.

Why do we even want "git-cp", especially when git-mv and git-rm
are already pretty much redundant commands?

Especially, why do we even encourage copy-and-paste?

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC/PATCH] Implement git-cp.
  2008-02-10  7:49         ` Junio C Hamano
@ 2008-02-10 12:33           ` Johannes Schindelin
  2008-02-10 12:42             ` Miklos Vajna
  2008-02-10 13:29           ` Robin Rosenberg
                             ` (2 subsequent siblings)
  3 siblings, 1 reply; 14+ messages in thread
From: Johannes Schindelin @ 2008-02-10 12:33 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Miklos Vajna, Jakub Narebski, Matthieu Moy, Francis Moreau, git

Hi,

On Sat, 9 Feb 2008, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > If you touch builtin-mv.c already, why not just move cmd_cp() in 
> > there?  It's not like it would be the first cmd_*() function living in 
> > the same file as other cmd_*() functions.
> 
> Why do we even want "git-cp", especially when git-mv and git-rm are 
> already pretty much redundant commands?
> 
> Especially, why do we even encourage copy-and-paste?

Heh.  I quote:

	If you want to shoot yourself in the foot Git will point you to 
	the gun rack and show you how to load the bullets. (Masukomi)

But yeah, I did not really think about it.  I had the impression 
originally that it would make sense to copy a few tracked files around, in 
order to modify them.

Think "kernel module".  You'd not start from scratch, but find a similar 
one, and then change the heck out of it.

But your comment made me think again: It's better to "cp" those 
files/directories, and then "git add" the relevant ones.  It even avoids 
stupid mistakes where you commit something you "cp"ed, but did not need.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC/PATCH] Implement git-cp.
  2008-02-10 12:33           ` Johannes Schindelin
@ 2008-02-10 12:42             ` Miklos Vajna
  0 siblings, 0 replies; 14+ messages in thread
From: Miklos Vajna @ 2008-02-10 12:42 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Junio C Hamano, Jakub Narebski, Matthieu Moy, Francis Moreau, git

[-- Attachment #1: Type: text/plain, Size: 764 bytes --]

On Sun, Feb 10, 2008 at 12:33:33PM +0000, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> But your comment made me think again: It's better to "cp" those 
> files/directories, and then "git add" the relevant ones.  It even avoids 
> stupid mistakes where you commit something you "cp"ed, but did not need.

that's exactly why i think git-cp makes sense. git cp copies the
directory tree and adds the tracked files to the index. if you use cp -R
to copy the directory tree then git add, you can easily add untracked
files, while git cp avoids this.

if you count gitignore, then it's like: why having git commit -a if we
have git add -u and git commit?

(if i'm right you mean: use cp -R and git add, instead of git cp)

thanks
- VMiklos

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC/PATCH] Implement git-cp.
  2008-02-10  7:49         ` Junio C Hamano
  2008-02-10 12:33           ` Johannes Schindelin
@ 2008-02-10 13:29           ` Robin Rosenberg
  2008-02-11  1:30           ` Jakub Narebski
  2008-02-11 10:18           ` Matthieu Moy
  3 siblings, 0 replies; 14+ messages in thread
From: Robin Rosenberg @ 2008-02-10 13:29 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Schindelin, Miklos Vajna, Jakub Narebski, Matthieu Moy,
	Francis Moreau, git

söndagen den 10 februari 2008 skrev Junio C Hamano:
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > If you touch builtin-mv.c already, why not just move cmd_cp() in there?  
> > It's not like it would be the first cmd_*() function living in the same 
> > file as other cmd_*() functions.
> 
> Why do we even want "git-cp", especially when git-mv and git-rm
> are already pretty much redundant commands?
> 
> Especially, why do we even encourage copy-and-paste?

We do not. I've seen too much damage being done through copy/paste. A
much better (safer) way to copy code is to re-type it. I've asked more
than once, when asked to help debug a piece of code: "Did you copy this 
code?".

For this reason I have a vision of a "plugin" for Eclipse that disables paste
if the clipboard contains copied code. Cut and paste would be ok.

-- robin

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH] Implement git-cp.
  2008-02-10  1:26       ` Johannes Schindelin
  2008-02-10  7:49         ` Junio C Hamano
@ 2008-02-10 18:24         ` Miklos Vajna
  1 sibling, 0 replies; 14+ messages in thread
From: Miklos Vajna @ 2008-02-10 18:24 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Schindelin, Jakub Narebski, Matthieu Moy, Francis Moreau, git

Actually it adds a -c option to git-mv to copy instead of move and add a
builtin alias git-cp for 'git mv -c'.

Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
---

On Sun, Feb 10, 2008 at 01:26:44AM +0000, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> >  builtin-cp.c                 |   24 ++++++++++
> >  builtin-mv.c                 |  104 ++++++++++++++++++++++++++++++++++++++++--
>
> If you touch builtin-mv.c already, why not just move cmd_cp() in there?
> It's not like it would be the first cmd_*() function living in the same
> file as other cmd_*() functions.

annotate vs blame was like this. but here is it, without builtin-cp.o.

 .gitignore                   |    1 +
 Documentation/git-cp.txt     |   42 +++++++++++++++
 Documentation/git-mv.txt     |   12 +---
 Documentation/mv-options.txt |    9 +++
 builtin-mv.c                 |  121 ++++++++++++++++++++++++++++++++++++++++--
 builtin.h                    |    1 +
 command-list.txt             |    1 +
 git.c                        |    1 +
 t/t7006-cp.sh                |   75 ++++++++++++++++++++++++++
 9 files changed, 249 insertions(+), 14 deletions(-)
 create mode 100644 Documentation/git-cp.txt
 create mode 100644 Documentation/mv-options.txt
 create mode 100755 t/t7006-cp.sh

diff --git a/.gitignore b/.gitignore
index 7f8421d..3d1dcdd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,7 @@ git-commit
 git-commit-tree
 git-config
 git-count-objects
+git-cp
 git-cvsexportcommit
 git-cvsimport
 git-cvsserver
diff --git a/Documentation/git-cp.txt b/Documentation/git-cp.txt
new file mode 100644
index 0000000..3109d04
--- /dev/null
+++ b/Documentation/git-cp.txt
@@ -0,0 +1,42 @@
+git-cp(1)
+=========
+
+NAME
+----
+git-cp - Copy a file, a directory, or a symlink
+
+SYNOPSIS
+--------
+'git-cp' <options>... <args>...
+
+DESCRIPTION
+-----------
+This script is used to copy a file, directory or symlink.
+
+ git-cp [-f] [-n] <source> <destination>
+ git-cp [-f] [-n] [-k] <source> ... <destination directory>
+
+In the first form, it copies <source>, which must exist and be either
+a file, symlink or directory, to <destination>.
+In the second form, the last argument has to be an existing
+directory; the given sources will be copied into this directory.
+
+The index is updated after successful completion, but the change must still be
+committed.
+
+
+OPTIONS
+-------
+include::mv-options.txt[]
+
+SEE ALSO
+--------
+linkgit:git-mv[1]
+
+AUTHOR
+------
+Written by Miklos Vajna <vmiklos@frugalware.org>.
+
+GIT
+---
+Part of the linkgit:git[7] suite
diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt
index bff3fbe..45d8255 100644
--- a/Documentation/git-mv.txt
+++ b/Documentation/git-mv.txt
@@ -27,15 +27,9 @@ committed.
 
 OPTIONS
 -------
--f::
-	Force renaming or moving of a file even if the target exists
--k::
-        Skip move or rename actions which would lead to an error
-	condition. An error happens when a source is neither existing nor
-        controlled by GIT, or when it would overwrite an existing
-        file unless '-f' is given.
--n, \--dry-run::
-	Do nothing; only show what would happen
+include::mv-options.txt[]
+-c::
+	Copy instead of move.
 
 
 Author
diff --git a/Documentation/mv-options.txt b/Documentation/mv-options.txt
new file mode 100644
index 0000000..ad78d1e
--- /dev/null
+++ b/Documentation/mv-options.txt
@@ -0,0 +1,9 @@
+-f::
+	Force renaming or moving of a file even if the target exists
+-k::
+        Skip move or rename actions which would lead to an error
+	condition. An error happens when a source is neither existing nor
+        controlled by GIT, or when it would overwrite an existing
+        file unless '-f' is given.
+-n, \--dry-run::
+	Do nothing; only show what would happen
diff --git a/builtin-mv.c b/builtin-mv.c
index 990e213..08a5526 100644
--- a/builtin-mv.c
+++ b/builtin-mv.c
@@ -60,14 +60,103 @@ static const char *add_slash(const char *path)
 	return path;
 }
 
+static int copyfile(const char *src_name, const char *dst_name)
+{
+	int ifd;
+	int ofd;
+	struct stat sb;
+	int cnt;
+	char buf[1024];
+
+	stat(src_name, &sb);
+	if ((ifd = open (src_name, O_RDONLY)) < 0)
+		die("could not open file %s for reading: %s", src_name, strerror(errno));
+	if ((ofd =
+	     open (dst_name, O_WRONLY | O_CREAT | O_TRUNC, 0)) < 0
+	    || chmod (dst_name, sb.st_mode & 07777))
+		die("could not open file %s for writing: %s", dst_name, strerror(errno));
+	while ((cnt = read (ifd, buf, sizeof buf)) > 0) {
+		if (write (ofd, buf, cnt) != cnt)
+			die("could not write to file %s", dst_name);
+	}
+	close (ifd);
+	close (ofd);
+	return 0;
+}
+
+static int copytree (const char *src_root, const char *dst_root)
+{
+	char src_name[1024];
+	char dst_name[1024];
+	int err = 0;
+	struct dirent *ent;
+	struct stat sb;
+	DIR *dir;
+
+	lstat(src_root, &sb);
+
+	if (!(dir = opendir (src_root)))
+		return copyfile(src_root, dst_root);
+	else
+		mkdir(dst_root, sb.st_mode);
+
+	while ((ent = readdir (dir))) {
+
+		if (strcmp (ent->d_name, ".") == 0 ||
+		    strcmp (ent->d_name, "..") == 0)
+			continue;
+
+		snprintf (src_name, sizeof src_name, "%s/%s", src_root,
+			  ent->d_name);
+
+		snprintf (dst_name, sizeof dst_name, "%s/%s", dst_root,
+			  ent->d_name);
+
+		if (lstat (src_name, &sb) < 0)
+			continue;
+
+		if (S_ISDIR (sb.st_mode)) {
+
+			if (mkdir (dst_name, sb.st_mode)
+			    || chmod (dst_name, sb.st_mode & 07777)
+			    || copytree (src_name, dst_name)) {
+				break;
+			}
+			continue;
+		}
+
+		if (S_ISLNK (sb.st_mode)) {
+			char oldlink[1024];
+			char dummy[1024];
+			int len;
+
+			if ((len =
+			     readlink (src_name, oldlink,
+				       sizeof (oldlink) - 1)) < 0)
+				die("could not read symlink %s: %s", src_name, strerror(errno));
+			oldlink[len] = '\0'; /* readlink() does not NULL-terminate */
+			if (symlink (oldlink, dst_name))
+				die("could not create symlink %s: %s", dst_name, strerror(errno));
+			continue;
+		}
+
+		if (copyfile(src_name, dst_name) == -1)
+			break;
+	}
+	closedir (dir);
+
+	return 0;
+}
+
 static struct lock_file lock_file;
 
 int cmd_mv(int argc, const char **argv, const char *prefix)
 {
 	int i, newfd;
-	int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
+	int verbose = 0, show_only = 0, copy = 0, force = 0, ignore_errors = 0;
 	struct option builtin_mv_options[] = {
 		OPT__DRY_RUN(&show_only),
+		OPT_BOOLEAN('c', NULL, &copy, "copy instead of move/rename"),
 		OPT_BOOLEAN('f', NULL, &force, "force move/rename even if target exists"),
 		OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"),
 		OPT_END(),
@@ -220,15 +309,20 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 		enum update_mode mode = modes[i];
 		if (show_only || verbose)
 			printf("Renaming %s to %s\n", src, dst);
-		if (!show_only && mode != INDEX &&
-				rename(src, dst) < 0 && !ignore_errors)
-			die ("renaming %s failed: %s", src, strerror(errno));
+		if (!show_only && mode != INDEX)
+		{
+			if(!copy && rename(src, dst) < 0 && !ignore_errors)
+				die ("renaming %s failed: %s", src, strerror(errno));
+			else if (copytree(src, dst) < 0 && !ignore_errors)
+				die ("copying %s failed: %s", src, strerror(errno));
+		}
 
 		if (mode == WORKING_DIRECTORY)
 			continue;
 
 		if (cache_name_pos(src, strlen(src)) >= 0) {
-			path_list_insert(src, &deleted);
+			if(!copy)
+				path_list_insert(src, &deleted);
 
 			/* destination can be a directory with 1 file inside */
 			if (path_list_has_path(&overwritten, dst))
@@ -271,3 +365,20 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 
 	return 0;
 }
+
+int cmd_cp(int argc, const char **argv, const char *prefix)
+{
+	const char **nargv;
+	int i;
+	nargv = xmalloc(sizeof(char *) * (argc + 2));
+
+	nargv[0] = "mv";
+	nargv[1] = "-c";
+
+	for (i = 1; i < argc; i++) {
+		nargv[i+1] = argv[i];
+	}
+	nargv[argc + 1] = NULL;
+
+	return cmd_mv(argc + 1, nargv, prefix);
+}
diff --git a/builtin.h b/builtin.h
index cb675c4..e1a85fe 100644
--- a/builtin.h
+++ b/builtin.h
@@ -28,6 +28,7 @@ extern int cmd_clean(int argc, const char **argv, const char *prefix);
 extern int cmd_commit(int argc, const char **argv, const char *prefix);
 extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
+extern int cmd_cp(int argc, const char **argv, const char *prefix);
 extern int cmd_describe(int argc, const char **argv, const char *prefix);
 extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
 extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
diff --git a/command-list.txt b/command-list.txt
index 3583a33..ddfcdaf 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -24,6 +24,7 @@ git-commit                              mainporcelain common
 git-commit-tree                         plumbingmanipulators
 git-config                              ancillarymanipulators
 git-count-objects                       ancillaryinterrogators
+git-cp                                  mainporcelain common
 git-cvsexportcommit                     foreignscminterface
 git-cvsimport                           foreignscminterface
 git-cvsserver                           foreignscminterface
diff --git a/git.c b/git.c
index 15fec89..f6a104b 100644
--- a/git.c
+++ b/git.c
@@ -298,6 +298,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "commit-tree", cmd_commit_tree, RUN_SETUP },
 		{ "config", cmd_config },
 		{ "count-objects", cmd_count_objects, RUN_SETUP },
+		{ "cp", cmd_cp, RUN_SETUP | NEED_WORK_TREE },
 		{ "describe", cmd_describe, RUN_SETUP },
 		{ "diff", cmd_diff },
 		{ "diff-files", cmd_diff_files },
diff --git a/t/t7006-cp.sh b/t/t7006-cp.sh
new file mode 100755
index 0000000..217072c
--- /dev/null
+++ b/t/t7006-cp.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+test_description='git cp in subdirs'
+. ./test-lib.sh
+
+test_expect_success \
+    'prepare reference tree' \
+    'mkdir path0 path1 &&
+     cp ../../COPYING path0/COPYING &&
+     git add path0/COPYING &&
+     git-commit -m add -a'
+
+test_expect_success \
+    'copying the file out of subdirectory' \
+    'cd path0 && git cp COPYING ../path1/COPYING'
+
+# in path0 currently
+test_expect_success \
+    'commiting the change' \
+    'cd .. && git-commit -m copy-out -a'
+
+test_expect_success \
+    'checking the commit' \
+    'git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \
+    grep "^C100..*path0/COPYING..*path1/COPYING"'
+
+test_expect_success \
+    'adding another file' \
+    'cp ../../README path0/README &&
+     git add path0/README &&
+     git-commit -m add2 -a'
+
+test_expect_success \
+    'copying whole subdirectory' \
+    'git cp path0 path2'
+
+test_expect_success \
+    'commiting the change' \
+    'git-commit -m dir-copy -a'
+
+test_expect_success \
+    'checking the commit' \
+    'git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \
+     grep "^C100..*path0/COPYING..*path2/COPYING" &&
+     git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \
+     grep "^C100..*path0/README..*path2/README"'
+
+test_expect_success \
+    'succeed when source is a prefix of destination' \
+    'git cp path2/COPYING path2/COPYING-copied'
+
+test_expect_success \
+    'copying whole subdirectory into subdirectory' \
+    'git cp path2 path1'
+
+test_expect_success \
+    'commiting the change' \
+    'git-commit -m dir-move -a'
+
+test_expect_success \
+    'checking the commit' \
+    'git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \
+     grep "^C100..*path0/COPYING..*path1/path2/COPYING" &&
+     git diff-tree -r -C --find-copies-harder --name-status  HEAD^ HEAD | \
+     grep "^C100..*path0/README..*path1/path2/README"'
+
+test_expect_success \
+    'do not copy directory over existing directory' \
+    'rm -rf path0 && mkdir path0 && mkdir path0/path2 && ! git cp path2 path0'
+
+test_expect_success \
+    'copy "."' \
+    'rm -rf path2 && git cp path1/path2/ .'
+
+test_done
-- 
1.5.4

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [RFC/PATCH] Implement git-cp.
  2008-02-10  7:49         ` Junio C Hamano
  2008-02-10 12:33           ` Johannes Schindelin
  2008-02-10 13:29           ` Robin Rosenberg
@ 2008-02-11  1:30           ` Jakub Narebski
  2008-02-11 10:18           ` Matthieu Moy
  3 siblings, 0 replies; 14+ messages in thread
From: Jakub Narebski @ 2008-02-11  1:30 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Schindelin, Miklos Vajna, Matthieu Moy, Francis Moreau, git

Junio C Hamano wrote:
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > If you touch builtin-mv.c already, why not just move cmd_cp() in there?  
> > It's not like it would be the first cmd_*() function living in the same 
> > file as other cmd_*() functions.
> 
> Why do we even want "git-cp", especially when git-mv and git-rm
> are already pretty much redundant commands?

git-rm is not redundant: without it how we could easily un-add
a file (without resorting to porcelain or tricks). I think it
is also safer that ordinary rm (check for being up-to-date, i.e.
for modified files).

The above also reflects git-mv, as half of it is git-rm (although
without some issues). Also git-mv could skip over non git-controlled
files.

> Especially, why do we even encourage copy-and-paste?

But git-cp would be IMHO a convenience only. The half of pros for
git-mv applies to this too; unfortunately the second part of pros
for git-mv does not apply for git-cp.

Would git-cp encourage copy'n'paste programming? I'm not sure...

-- 
Jakub Narebski
Poland

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC/PATCH] Implement git-cp.
  2008-02-10  7:49         ` Junio C Hamano
                             ` (2 preceding siblings ...)
  2008-02-11  1:30           ` Jakub Narebski
@ 2008-02-11 10:18           ` Matthieu Moy
  2008-02-11 13:43             ` Miklos Vajna
  3 siblings, 1 reply; 14+ messages in thread
From: Matthieu Moy @ 2008-02-11 10:18 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Schindelin, Miklos Vajna, Jakub Narebski, Francis Moreau, git

Junio C Hamano <gitster@pobox.com> writes:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>
>> If you touch builtin-mv.c already, why not just move cmd_cp() in there?  
>> It's not like it would be the first cmd_*() function living in the same 
>> file as other cmd_*() functions.
>
> Why do we even want "git-cp", especially when git-mv and git-rm
> are already pretty much redundant commands?
>
> Especially, why do we even encourage copy-and-paste?

I rarely (let's say: never ;-) ) copy-and-paste a C source file, but
there are tons of cases wher I start a file by copying another
(typically, a 10 lines long Makefile containing macro definitions and
an "include" statement, to be copied in each subdirectory and adapted
to reflect the source file name, or starting a .txt file based on a
skeleton).

I can very well live without a "git cp" command, but I'd use it if it
was available.

-- 
Matthieu

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC/PATCH] Implement git-cp.
  2008-02-11 10:18           ` Matthieu Moy
@ 2008-02-11 13:43             ` Miklos Vajna
  0 siblings, 0 replies; 14+ messages in thread
From: Miklos Vajna @ 2008-02-11 13:43 UTC (permalink / raw)
  To: Matthieu Moy
  Cc: Junio C Hamano, Johannes Schindelin, Jakub Narebski, Francis Moreau, git

[-- Attachment #1: Type: text/plain, Size: 445 bytes --]

On Mon, Feb 11, 2008 at 11:18:21AM +0100, Matthieu Moy <Matthieu.Moy@imag.fr> wrote:
> I rarely (let's say: never ;-) ) copy-and-paste a C source file, but
> I can very well live without a "git cp" command, but I'd use it if it
> was available.

same here. i can live without git add -u and git cp but they are both
quite handy and i'm happy that git add -u is in git.git. i think other
people would use it sometimes as well.

thanks,
- VMiklos

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2008-02-11 13:44 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-02-03 18:23 What about git cp ? Francis Moreau
2008-02-03 18:46 ` Matthieu Moy
2008-02-03 19:07   ` Jakub Narebski
2008-02-10  1:12     ` [RFC/PATCH] Implement git-cp Miklos Vajna
2008-02-10  1:26       ` Johannes Schindelin
2008-02-10  7:49         ` Junio C Hamano
2008-02-10 12:33           ` Johannes Schindelin
2008-02-10 12:42             ` Miklos Vajna
2008-02-10 13:29           ` Robin Rosenberg
2008-02-11  1:30           ` Jakub Narebski
2008-02-11 10:18           ` Matthieu Moy
2008-02-11 13:43             ` Miklos Vajna
2008-02-10 18:24         ` [PATCH] " Miklos Vajna
2008-02-03 18:55 ` What about git cp ? Remi Vanicat

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.