如何按日期自动执行git历史南瓜?

时间:2019-07-02 11:35:46

标签: bash git git-rebase

我有一个用作文件夹同步系统的git存储库:每当我在便携式计算机,个人计算机或移动设备中更改文件中的内容时,更改都会自动提交。没有分支,单个用户。

这会导致大量提交,例如每天50次。我想编写一个bash cron脚本来自动执行历史记录压缩,每天只提交一次,与评论无关,但保留日期。

我尝试了git-rebase -i SHA~count,但是我不知道如何自动执行该过程,即选择第一个提交并压缩其他计数提交。

有什么建议吗?

我写关于查找日期的第一个SHA的bash并合并提交的计数没有问题,对此进行一些循环将达到目的:

git log --reverse|grep -E -A3 ^commit| \
  grep -E -v 'Merge|Author:|--|^$'|paste - -| \
  perl -pe 's/commit (\w+)\s+Date:\s+\w+\s+(\w+)\s+(\d+).+/\2_\3 \1/'

3 个答案:

答案 0 :(得分:2)

根据我的理解,您打算按照以下方式做些事情:

#!/bin/bash
FIRST_COMMIT_HASH_TODAY="$(git log --since="1 days ago" --pretty=format:%H | tail -n 1)"
git reset --soft ${FIRST_COMMIT_HASH_TODAY}^
git commit -m "Squashed changes for $(date +%F)"

即。

  1. 列出在最后一天发生的所有提交的提交哈希,并提取其中的第一个提交哈希。
    (假设每天至少有一次提交,采用上述当前格式)
  2. 将存储库的 HEAD 指针移至 $ FIRST_COMMIT_HASH_OF_THE_DAY 之前的提交,但保持工作树和索引不变。
  3. 提交被压缩的更改。

一个警告语 ...请注意,现在您正在有效地重写历史记录。您不能再只是git pull 同步更改,因为如果客户端存储库仍具有原始提交历史记录,而服务器具有重写的历史记录, 您将得到类似:

Your branch and 'origin/master' have diverged,                                                                                                                                                                                                                                  
and have 50 and 1 different commit(s) each, respectively.

如果要处理整个历史记录,一种方法是使用git filter-branch的某些变体。我在下面放了一个示例方法,但是这种方法有很多缺点,因此您可能需要对其进行一些改进。

弱点/特征:

  • 仅忽略git raw时间戳中的时区。 (如果在不同时区进行提交,则行为很奇怪)
  • 通过其根树哈希标识要处理的分支上的最新提交。 (如果多个提交具有相同的根树,则行为很奇怪(例如,一个还原提交还原了其父提交))
  • 假设线性分支历史。 (如果分支中有合并提交,则行为很奇怪)
  • 不专门创建每天一次提交。相反,对于每次提交,它都会检查自上一次提交以来是否至少已经过24小时。如果没有,则跳过该提交。
  • 始终保留第一个和最后一个提交,无论它们是否及时与后续/先前的提交接近。
  • 基于GIT_COMMITER_DATE而不是GIT_AUTHOR_DATE进行工作。
  • 未经良好测试。因此,如果要尝试运行此原始存储库,请确保备份原始存储库。

示例命令:

LATEST_TREE=$(git rev-parse HEAD^{tree}) git filter-branch --commit-filter '
  # $3 = parent commit hash (if commit has at least one parent)
  if [ -z "$3" ] 
  then
    # First commit. Keep it.
    git commit-tree "$@"
  elif [ "$1" == "$LATEST_TREE" ]
  then
    # Latest commit. Keep it.
    git commit-tree "$@"
  else
    PREVIOUS_COMMIT_COMMITTER_DATE="$(git log -1 --date=raw --pretty=format:%cd $3)"
    PREVIOUS_COMMIT_COMMITTER_DATE_NO_TIMEZONE="$(echo $PREVIOUS_COMMIT_COMMITTER_DATE | egrep -o "[0-9]{5,10}")"
    GIT_COMMITTER_DATE_NO_TIMEZONE="$(echo $GIT_COMMITTER_DATE | egrep -o "[0-9]{5,10}")"
    SECONDS_PER_DAY="86400"

    if [ $(expr $GIT_COMMITTER_DATE_NO_TIMEZONE - $PREVIOUS_COMMIT_COMMITTER_DATE_NO_TIMEZONE) -gt $SECONDS_PER_DAY ]
    then
      # 24 hours elapsed since previous commit. Keep this commit.
      git commit-tree "$@"
    else
      skip_commit "$@"
    fi
  fi' HEAD

如果您有一个命令要提取要保留的提交的提交哈希,也许您可​​以获取所有这些提交的根树哈希,并将它们存储到单独的文件中。然后,您可以更改提交筛选条件,以检查“当前的根树哈希是否存在于所需的根树哈希文件中?”而不是“自上一次提交以来已经过了24小时?”。 (不过,这会放大我上面提到的“通过根树哈希识别提交”问题,因为它适用于所有提交,而不仅仅是最新提交)

答案 1 :(得分:0)

如果您有想要返回的提交数量,则可以使用git reset --soft然后进行新的提交,例如

COMMIT_COUNT=$(git log --pretty=oneline --since="1 days" | wc -l) 
git reset --soft HEAD~$COMMIT_COUNT
git commit -m "Today's work" 

答案 2 :(得分:0)

我分享基于Alderath建议的结果:我使用git filter-branch来解析历史记录并保留当天的最后一次提交。 git log上的第一个循环将需要保留的提交时间戳(一天中的最后一个)写入临时文件;然后使用git filter-branch,我只保留文件中包含时间戳的提交。

#!/bin/bash

# extracts the timestamps of the commits to keep (the last of the day)
export TOKEEP=`mktemp`
DATE=
for time in `git log --date=raw --pretty=format:%cd|cut -d\  -f1` ; do
   CDATE=`date -d @$time +%Y%m%d`
   if [ "$DATE" != "$CDATE" ] ; then
       echo @$time >> $TOKEEP
       DATE=$CDATE
   fi
done

# scan the repository keeping only selected commits
git filter-branch -f --commit-filter '
    if grep -q ${GIT_COMMITTER_DATE% *} $TOKEEP ; then
        git commit-tree "$@"
    else
        skip_commit "$@"
    fi' HEAD
rm -f $TOKEEP