重写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
遍历符合条件的提交,执行指定命令修改代码库,并将修改后的代码提交到代码库,实现重写记录的目标

前提条件:

  1. 警告:一般在提交记录没有推送到服务器之前,我们可以重写提交记录;一旦提交记录已经推送到了服务器,分享给了其他团队成员,不建议修改历史记录,否则会导致历史记录的混乱,甚至有可能导致代码混乱。
  2. 由于涉及到修改服务器端的历史记录,你的操作账户必须具备“强制推送”的权限,即,可以可以重写历史记录。
    在Azure DevOps Server 中,这项权限默认是未设置状态(如下图),一般的用户都不允许重写历史记录。
    image

2. 应用场景一:修改某开发人员的电子邮件

场景说明
在开发过程中,某用户忘记修改本地的用户名称和电子邮箱信息,做了大量的提交,并推送到了服务器,后来发现服务器上显示不正确,需要修改为正确的邮箱地址。如下图,我需要显示自己的中文姓名和邮箱地址

image

操作方法

  1. 首先,将代码克隆到本地计算机;如果正在开发的代码库,不需要重复克隆。
  2. 使用Git Bash打开代码库
  3. 在命令行中运行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

image
4. 使用强制方式推送本地的更改

git push --force

image
5. 检查服务器上的更改
在Repo-提交中查看服务器的历史记录,你会发现用户的姓名已经发生了变化;
如果你仔细比较提交ID,会发现也发生了变化。

image

3. 应用场景二:删除服务器中的大文件

场景说明
单用户在提交文件时间,不小心将大文件例如视频推送到了服务器,导致其他用户克隆、更新非常满,流水线运行的速度也明显降低。
现在我们需要将用户提交的大文件彻底删除。
操作方法

  1. 克隆代码库到本地
    在下面的这个示例中,由于代码库中存在一个300MB左右的视频文件(a.mp4),克隆的速度明显很慢
    image
  2. 使用--tree-filter参数删除视频文件
git filter-branch --tree-filter "rm -f a.mp4"

image
可以看到,Git遍历了当前分支中的所有提交记录,并将每个提交中的视频文件删除,并重新提交;通过这种方式重新修改了提交历史记录。

  1. 使用--force参数将修改后的提交推送到服务器中
git push origin master --force

推送完成后,如果你比较推送前后的提交历史记录,你会发现ID(sha值)已经发生了变化。

  1. 如果确认这种修改没有问题,可以在filter-branch命令的后面增加--all参数,在所有分支中移除文件,实现完全删除大文件,提高克隆速度的目标。
git filter-branch --tree-filter "rm -f a.mp4" -- --all

image
注意,在--all前面添加--
你会发现执行这次命令后,test分支发生的修改(见上图)

4. 用户场景三:整理代码库的目录结构

场景说明
在代码库中,由于所有文件都是从svn导入过来的,出现了trunk, tags,releases等目录(如下图),需要将trunk文件夹中的内容,移动到根目录中,作为项目开发的根目录。
image

操作方法

  1. 首先克隆代码库,方式与上面的描述一样
  2. 使用git filter-branch --subdirectory-filter
git filter-branch --subdirectory-filter trunk HEAD

image
可以看到本地工作区的文件夹结构已经发生了变化
3. 将本地的变更推送(Push)到服务器

git push --force
  1. 验证服务器上的文件已经发生的变化

  2. 如果需要在所有分支上处理文件夹结构,可以使用--all参数,再次强制推送

git filter-branch --subdiretory-filter trunk -- --all

4. 常见问题

1. 系统提示 ! [rejected] ,没有权限

如下图
image
解决方案
一般是服务器中没有为用户配置强制推送的权限,修改权限后问题消失

2. 其他开发人员拉取(PULL)代码时,出现错误refusing to merge unrelated histories

错误信息如下图:
image
解决方案:
在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 张洪君

------------------------------------------------------------

posted on 2020-07-05 14:00  danzhang  阅读(607)  评论(0编辑  收藏  举报

导航