如何实现'pre-checkout' hook in Git/bitbucket?

How to achieve 'pre-checkout' hook in Git/bitbucket?

网上查了很多,结果是pre-checkout hook in git还没有实现。原因可能是:

我的问题是:

我已经实施了 pre-commitpost-mergepost-checkout 挂钩来维护每个分支的不同数据库备份。

场景:现在当我提交数据库备份时使用pre-commit挂钩保存在一个文件中。当我检查分支或合并发生时,存储在文件中的数据库使用 post-merge & post-checkout 挂钩恢复。现在的情况是,如果有人在提交和检出后对数据库进行了更改,更改就会丢失,因为数据库没有备份。签出成功,因为文件结构没有变化。

所以在这种情况下,我想要一个 pre-checkout 挂钩来处理上述场景中备份数据库的任务。

没有实现的原因我知道了。在我的情况下,我确实备份了数据库并将其存储在一个文件中,这每次都会导致签出失败。因此,实现此功能是不切实际的。

您可以编写一个脚本来执行备份然后结帐。然后创建一个 shell 别名,以便它在您键入 git checkout.

时运行该脚本

结帐前用例:删除由 gulp watch:css 任务生成的未版本化的本地文件,以便它们在结帐后被覆盖并由 gulp 重建。

相反,我将编写一个 shell 脚本来获取存储库根目录,然后删除找到的 gulp 生成文件列表中的任何一个。

我有一个用例,其中也需要预结帐挂钩(我们有开发人员滥用 git checkout -B 和 git push --force 导致各种噩梦)。 Git 开始时没有很多挂钩,所以我也使用相同的解决方案在不修改二进制文件的情况下向 git 添加新挂钩。

注意:此解决方案要求您将 /usr/bin/git 重命名为 /usr/bin/git.real 并将其替换为 PHP 脚本。因此,如果您将其设置为在其中显示 Git 状态,它将稍微减慢命令提示符的显示速度,例如:test@Work-VirtualBox /var/www/html/someApp (master) #

这些挂钩对于 运行 机器上的 TRUE“Pre”和“Post”挂钩非常有用 运行ning 命令(不是在远程仓库上)。如果您需要在 git 命令期间触发钩子,请不要使用它们。您必须使用真正的挂钩来实际挂接到 git ,同时它正在处理命令(例如 git 提交消息挂钩,远程仓库上的挂钩等)。为什么 git 没有对每个命令都有挂钩,我不知道。这会很有用。此解决方案也可以采用任何语言(bash、perl、ruby 等),我只是更喜欢 PHP,因为它是我最了解的。

要使用,运行以下(假设是linux平台)


    mv /usr/bin/git /usr/bin/git.real
    chmod +x /usr/bin/git.php
    ln -s /usr/bin/git.php /usr/bin/git
    git status

#!/usr/bin/php
<?php
init();
//Process pre hooks IE:  pre_checkout
process_hooks('pre_');

//Process the git command itself
process_git_command();

//Process post hooks: IE: post_checkout
process_hooks('post_');

function pre_status()
{
   global $allow_cmd;
   echo "PRE-STATUS HOOK FIRED SUCCESSFULLY\n";
   $allow_cmd = TRUE; //Change to false to cause an example error
}

function post_status()
{
   global $allow_cmd;
   echo "POST-STATUS HOOK FIRED SUCCESSFULLY\n";
   $allow_cmd = TRUE;
}

function init()
{
   global $argv;
   unset($argv[0]);

   $GLOBALS['git'] = '/usr/bin/git.real'; //New path to the real Git binary
   $GLOBALS['cmd'] = parse_cmd($argv);
   $GLOBALS['cmd_orig'] = $argv;
   $GLOBALS['cmd_orig_str'] = implode(' ', $argv);
   $GLOBALS['cmd_without_hook'] = array();

   //Default to allowing ANYTHING as long as it passes the defined hooks,  change to FALSE if you want to really lock down the git command hard.  It would only allow git commands that have passed all git hooks defined for the command that was run.  Useful for forcing a git command line standards.
   $GLOBALS['allow_cmd'] = TRUE;

   //Define the hooks we'll use.  These will have pre- and post- hooks.
   $GLOBALS['hooks'][] = 'checkout';
   $GLOBALS['hooks'][] = 'commit';
   $GLOBALS['hooks'][] = 'add';
   $GLOBALS['hooks'][] = 'revert';
   $GLOBALS['hooks'][] = 'push';
   $GLOBALS['hooks'][] = 'pull';
   $GLOBALS['hooks'][] = 'merge';
   $GLOBALS['hooks'][] = 'rm';
   $GLOBALS['hooks'][] = 'mv';
   $GLOBALS['hooks'][] = 'status'; //Used mostly as a test hook
}

//Parse the cmdline into a switches and arguments array
function parse_cmd($cmd_arr)
{
   $cmd = array();
   $cmd['switches'] = array();
   $cmd['arguments'] = array();

   if (!empty($cmd_arr) && is_array($cmd_arr))
   {
      foreach ($cmd_arr AS $key => $value)
      {
         if (!empty($value))
         {
            if ($value[0] == '-')
            {
               $cmd['switches'][$key] = $value;
            }
            else
            {
               $cmd['arguments'][$key] = $value;
            }
         }
      }
   }
   return $cmd;
}

function process_git_command()
{
   global $allow_cmd;
   global $git;
   global $cmd_orig_str;

   if ($allow_cmd == TRUE)
   {
      passthru("{$git} {$cmd_orig_str}");
   }
   else
   {
      echo "ERROR, something went wrong\n"; //Modify as needed.
      exit(1);
   }
}

function process_hooks($type)
{
   global $hooks;
   global $cmd;
   global $cmd_without_hook;
   $cmd_without_hook = $cmd;

   // See if there is an argument that matches a hook,  if so, process it.
   foreach ($cmd['arguments'] AS $key => $arg)
   {
      if (!empty($arg) && in_array($arg, $hooks) && function_exists("{$type}{$arg}"))
      {

         //Remove the git hook from the command line (usually the command such as "checkout").  Makes parsing the command line a little easier/cleaner in the hook functions.
         unset($cmd_without_hook['arguments'][$key]);
         //The hook was found in the arguments, so fire it
         $function = "{$type}{$arg}";
         $function();
      }
   }
}

你可以模拟一个pre-checkoutgit钩子:

#!/bin/bash
# .git/hooks/pre-checkout

export PRE_CHECKOUT=true
git switch --quiet -
BRANCH=$(git branch --show-current)
echo "first go back to $BRANCH branch to do stuff"
git switch --quiet -

post-checkout 调用这个脚本,它会切换回源分支做一些事情,然后再回到目标分支。它使用 PRE_CHECKOUT 环境变量来跟踪自己的执行,因此 post-checkout 挂钩需要一些逻辑只执行一次:

#!/bin/bash
# .git/hooks/post-checkout

# confirm the environment variable exists and it is 'true'
if [ ! -z $PRE_CHECKOUT ] && $PRE_CHECKOUT
then
    exit 0
fi

. .git/hooks/pre-checkout
BRANCH=$(git branch --show-current)
echo "now in $BRANCH branch doing post-checkout stuff"