我读到在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时没有识别出这个重命名呢?
最好的办法是自己去试试。
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”。如果它显示文件已重命名,那么就可以开始了。
当我把我的文件夹和文件大写时,我使用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脚本文件或命令
对于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状态可能不会。