重写Azure DevOps Server (TFS)中的Git版本记录
Contents
1. 概述
2. 应用场景一:修改某开发人员的电子邮件
3. 应用场景二:删除服务器中的大文件
4. 用户场景三:整理代码库的目录结构
4. 常见问题
1. 系统提示 ! [rejected] ,没有权限
2. 其他开发人员拉取(PULL)代码时,出现错误refusing to merge unrelated histories
3. 重写服务器上的提交记录后,其他成员应该怎么办?
4. 在所有分支中删除文件,需要增加参数 --all
5. 错误:Cannot create a new backup
1. 概述
一般情况下,提交到了服务器中的版本记录,是不允许修改的。这也是为什么在使用Azure DevOps Server (之前名为TFS)的时候,当我们已经将本地提交(Commit)推送到了服务器上,Azure DevOps Server 找不到在线修改服务器历史记录的方法;如果确实需要修改代码库的历史记录,例如修改提交人、删除大文件等,就需要使用Git的相关命令行功能。本文介绍如何使用git的filter-branch 工具在Azure DevOps Server 中重写服务器端的提交记录。实际上,本文中介绍的方法也适用于大部分Git服务器,例如Github和GitLab等。
filter-branch 有三个常用的参数:
1. --tree-filter
这个选项在检出(checkout)项目的每一个提交后运行指定的命令然后重新提交结果。
例如,在后面的演示场景中,我们要移除代码库中一个大文件视频,就是使用了这个参数。
2. --subdirectory-filter
这个选项使一个子目录做为新的根目录,例如从svn导入过来的代码库,需要去掉tag/release等文件夹,使用trunk作为根目录
3. --commit-filter
遍历符合条件的提交,执行指定命令修改代码库,并将修改后的代码提交到代码库,实现重写记录的目标
前提条件:
- 警告:一般在提交记录没有推送到服务器之前,我们可以重写提交记录;一旦提交记录已经推送到了服务器,分享给了其他团队成员,不建议修改历史记录,否则会导致历史记录的混乱,甚至有可能导致代码混乱。
- 由于涉及到修改服务器端的历史记录,你的操作账户必须具备“强制推送”的权限,即,可以可以重写历史记录。
在Azure DevOps Server 中,这项权限默认是未设置状态(如下图),一般的用户都不允许重写历史记录。
2. 应用场景一:修改某开发人员的电子邮件
场景说明
在开发过程中,某用户忘记修改本地的用户名称和电子邮箱信息,做了大量的提交,并推送到了服务器,后来发现服务器上显示不正确,需要修改为正确的邮箱地址。如下图,我需要显示自己的中文姓名和邮箱地址
操作方法
- 首先,将代码克隆到本地计算机;如果正在开发的代码库,不需要重复克隆。
- 使用Git Bash打开代码库
- 在命令行中运行git filter-branch命令,具体目录格式和参数如下
git filter-branch --commit-filter ' if [ "$GIT_AUTHOR_EMAIL" = "zhanghongjun@example.com" ]; then GIT_AUTHOR_NAME="张洪君"; GIT_AUTHOR_EMAIL="zhanghongjun@bjgreatsoft.com"; git commit-tree "$@"; else git commit-tree "$@"; fi' HEAD
git push --force
5. 检查服务器上的更改
在Repo-提交中查看服务器的历史记录,你会发现用户的姓名已经发生了变化;
如果你仔细比较提交ID,会发现也发生了变化。
3. 应用场景二:删除服务器中的大文件
场景说明
单用户在提交文件时间,不小心将大文件例如视频推送到了服务器,导致其他用户克隆、更新非常满,流水线运行的速度也明显降低。
现在我们需要将用户提交的大文件彻底删除。
操作方法
git filter-branch --tree-filter "rm -f a.mp4"
可以看到,Git遍历了当前分支中的所有提交记录,并将每个提交中的视频文件删除,并重新提交;通过这种方式重新修改了提交历史记录。
- 使用--force参数将修改后的提交推送到服务器中
git push origin master --force
推送完成后,如果你比较推送前后的提交历史记录,你会发现ID(sha值)已经发生了变化。
- 如果确认这种修改没有问题,可以在filter-branch命令的后面增加--all参数,在所有分支中移除文件,实现完全删除大文件,提高克隆速度的目标。
git filter-branch --tree-filter "rm -f a.mp4" -- --all
注意,在--all前面添加--
你会发现执行这次命令后,test分支发生的修改(见上图)
4. 用户场景三:整理代码库的目录结构
场景说明
在代码库中,由于所有文件都是从svn导入过来的,出现了trunk, tags,releases等目录(如下图),需要将trunk文件夹中的内容,移动到根目录中,作为项目开发的根目录。
操作方法
- 首先克隆代码库,方式与上面的描述一样
- 使用git filter-branch --subdirectory-filter
git filter-branch --subdirectory-filter trunk HEAD
可以看到本地工作区的文件夹结构已经发生了变化
3. 将本地的变更推送(Push)到服务器
git push --force
验证服务器上的文件已经发生的变化
如果需要在所有分支上处理文件夹结构,可以使用--all参数,再次强制推送
git filter-branch --subdiretory-filter trunk -- --all
4. 常见问题
1. 系统提示 ! [rejected] ,没有权限
如下图
解决方案
一般是服务器中没有为用户配置强制推送的权限,修改权限后问题消失
2. 其他开发人员拉取(PULL)代码时,出现错误refusing to merge unrelated histories
错误信息如下图:
解决方案:
在git pull 命令下增加参数--allow-unrelated-histories,如下图:
git pull --allow-unrelated-histories
3. 重写服务器上的提交记录后,其他成员应该怎么办?
最好的方法是重新克隆代码库,否则会发现日志记录混乱;还可能会不小心将之前的提交又推送到了服务器上。
4. 在所有分支中删除文件,需要增加参数 --all
注意:增加--all,并且前面有两个短横线,例如
git filter-branch --tree-filter "" -- --all
注意,这里有四个短横线,前两个短横线是指定范围的参数,后面是指定的范围--all
5. 错误:Cannot create a new backup
如果第二次运行filter-branch,系统会提示错误信息“Cannot create a new backup.”
这是由于第一次运行filter-branch的时候,系统已经创建了备份,例如:
git filter-branch -f --all --tree-filter "rm -f a.mp4"
注意-f参数的位置,不能放到命令的最后面
------------------------------------------------------------
http://www.cnblogs.com/danzhang/ DevOps MVP 张洪君
------------------------------------------------------------