* 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: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 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
* 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
* [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 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: 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
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.