我读到在Git中重命名文件时,你应该提交任何更改,执行重命名,然后展示重命名的文件。Git将从内容中识别文件,而不是将其视为一个新的未跟踪的文件,并保留更改历史。
然而,今晚这样做,我最终恢复到git mv。
> $ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: index.html
#
我重命名我的样式表在Finder从iphone.css到mobile.css:
> $ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: index.html
#
# Changed but not updated:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted: css/iphone.css
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# css/mobile.css
所以Git现在认为我删除了一个CSS文件,并添加了一个新的文件。这不是我想要的。让我们撤销重命名,让Git来完成这项工作。
> $ git reset HEAD .
Unstaged changes after reset:
M css/iphone.css
M index.html
我又回到了开始的地方:
> $ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: index.html
#
让我们用git mv代替:
> $ git mv css/iphone.css css/mobile.css
> $ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# renamed: css/iphone.css -> css/mobile.css
#
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: index.html
#
看起来我们没事了。那么,为什么Git在我第一次使用Finder时没有识别出这个重命名呢?
对于git mv手册页说
索引在成功完成后更新,
[…]
因此,首先,您必须自己更新索引
(使用git添加mobile.css)。但是git状态
仍然会显示两个不同的文件:
$ git status
# On branch master
warning: LF will be replaced by CRLF in index.html
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: index.html
# new file: mobile.css
#
# Changed but not updated:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted: iphone.css
#
你可以通过运行git commit——dry-run -a来得到不同的输出
期望:
Tanascius@H181 /d/temp/blo (master)
$ git commit --dry-run -a
# On branch master
warning: LF will be replaced by CRLF in index.html
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: index.html
# renamed: iphone.css -> mobile.css
#
我不能确切地告诉你为什么我们会看到这些差异
在git状态和git提交之间——dry-run -a,但是
下面是莱纳斯的提示:
Git根本不关心整体
在内部“重命名检测”,以及您拥有的任何提交
所做的重命名完全独立于
然后我们使用启发式来显示重命名。
试运行使用真正的重命名机制,而
Git状态可能不会。
最好的办法是自己去试试。
mkdir test
cd test
git init
touch aaa.txt
git add .
git commit -a -m "New file"
mv aaa.txt bbb.txt
git add .
git status
git commit --dry-run -a
现在git状态和git提交——dry-run -a显示两个不同的结果,其中git状态显示bbb.txt作为一个新文件/ aaa.txt被删除,而——dry-run命令显示实际的重命名。
~/test$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: bbb.txt
#
# Changes not staged for commit:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted: aaa.txt
#
/test$ git commit --dry-run -a
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# renamed: aaa.txt -> bbb.txt
#
现在去办理登机手续。
git commit -a -m "Rename"
现在您可以看到文件实际上已重命名,并且git status中显示的内容是错误的,至少在本例中是这样。下划线GIT实现可能会将这两个命令分开处理。
这个故事的寓意:如果你不确定你的文件是否被重命名了,发出一个“git commit -dry-run -a”。如果它显示文件已重命名,那么就可以开始了。
让我们从Git的角度来考虑您的文件。
请记住,Git不会跟踪任何关于文件的元数据
您的存储库具有(以及其他)
$ cd repo
$ ls
...
iphone.css
...
它在Git的控制下:
$ git ls-files --error-unmatch iphone.css &>/dev/null && echo file is tracked
file is tracked
用以下方法进行测试:
$ touch newfile
$ git ls-files --error-unmatch newfile &>/dev/null && echo file is tracked
(no output, it is not tracked)
$ rm newfile
当你这样做时
$ mv iphone.css mobile.css
从Git的角度来看,
没有任何iphone.css(它被删除了-git对此发出警告-)。
有一个新文件mobile。css。
这些文件完全不相关。
因此,Git会对它已经知道的文件(iphone.css)和它检测到的新文件(mobile.css)提出建议,但只有当文件位于index或HEAD中时,Git才会开始检查它们的内容。
目前,“iphone.css deletion”和“mobile.css”都不在索引中。
将iphone.css delete添加到索引中:
$ git rm iphone.css
Git告诉你到底发生了什么(iphone.css被删除。没有别的事情发生):
然后添加新的文件mobile.css:
$ git add mobile.css
这次,删除文件和新建文件都在索引中。现在Git检测到上下文是相同的,并将其公开为重命名。事实上,如果文件有50%的相似度,它会将其检测为重命名,允许您稍微更改mobile.css,同时保持操作的重命名。
这在git diff上是可复制的,现在你的文件在索引中,你必须使用-cached。编辑mobile.css一点,将其添加到索引,并查看以下区别:
$ git diff --cached
and
$ git diff --cached -M
-M是git diff的“检测重命名”选项。-M代表-M50%(50%或更多的相似度将使git表示为重命名),但如果你经常编辑mobile.css文件,你可以将其减少为-M20%(20%)。
当我把我的文件夹和文件大写时,我使用Node.js的文件系统来解决这个问题。
在开始之前,不要忘记做出承诺。我不确定它有多稳定:)
在项目根中创建脚本文件。
// File rename.js
const fs = require('fs').promises;
const util = require('util');
const exec = util.promisify(require('child_process').exec);
const args = process.argv.slice(2);
const path = args[0];
const isCapitalized = (s) => s.charAt(0) === s.charAt(0).toUpperCase();
const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
async function rename(p) {
const files = await fs.readdir(p, { withFileTypes: true });
files.forEach(async (file) => {
const { name } = file;
const newName = capitalize(name);
const oldPath = `${p}/${name}`;
const dumbPath = `${p}/dumb`;
const newPath = `${p}/${newName}`;
if (!isCapitalized(name) && name !== 'i18n' && name !== 'README.md') {
// 'git mv' won't work if we only changed size of letters.
// That's why we need to rename to dumb name first, then back to current.
await exec(`git mv ${oldPath} ${dumbPath}`);
await exec(`git mv ${dumbPath} ${newPath}`);
}
if (file.isDirectory()) {
rename(newPath);
}
});
}
rename(path);
重命名目录(或单个文件)脚本中的所有文件更简单:
// rename.js
const fs = require('fs').promises;
const util = require('util');
const exec = util.promisify(require('child_process').exec);
const args = process.argv.slice(2);
const path = args[0];
async function rename(p) {
const files = await fs.readdir(p, { withFileTypes: true });
files.forEach(async (file) => {
const { name } = file;
const currentPath = `${p}/${name}`;
const dumbPapth = `${p}/dumb`;
await exec(`git mv ${currentPath} ${dumbPath}`);
await exec(`git mv ${dumbPath} ${currentPath}`);
if (file.isDirectory()) {
rename(newPath);
}
});
}
rename(path);
运行带有参数的脚本
node rename.js ./frontend/apollo/queries
如何使用Node.js运行shell脚本文件或命令