如何在Git/bitbucket中实现“预 checkout ”钩子?

vuv7lop3  于 2023-01-01  发布在  Git
关注(0)|答案(5)|浏览(194)

网上大量搜索的结果是git中的pre-checkout钩子还没有实现,原因可能是:

  • 没有实际用途。我确实有一个案例
  • 它可以通过任何其他方式实现。请告诉我如何实现?
  • 太难实施了。我不认为这是一个合理的理由

我的问题是:
我已经实现了pre-commitpost-mergepost-checkout钩子,用于维护每个分支的不同数据库备份。

    • 情景**:现在,当我提交数据库备份保存在一个文件中使用pre-commit挂钩。当我 checkout 分支或合并发生时,存储在文件中的数据库恢复使用post-mergepost-checkout挂钩。现在的情况是,如果有人在提交和 checkout 后在数据库中进行更改,更改丢失,因为数据库未备份。 checkout 成功,因为文件结构没有更改。

因此,在本例中,我需要一个pre-checkout钩子来处理上述场景中备份数据库的任务。

g9icjywg

g9icjywg1#

你可以写一个脚本来备份然后 checkout ,然后创建一个shell别名,这样当你输入git checkout时,它就会运行那个脚本。

ruarlubt

ruarlubt2#

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

#!/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"
mkshixfv

mkshixfv3#

预检验用例:删除gulp watch:css任务生成的未版本化本地文件,以便在检出后覆盖这些文件并由gulp重建。
相反,我将编写一个shell脚本,获取repo根目录,然后删除找到的gulp生成的文件列表中的任何文件。

r6vfmomb

r6vfmomb4#

我知道了为什么没有实现它的原因。在我的情况下,我备份数据库并将其存储在一个文件中,这将失败 checkout 每一次。所以这将是不切实际的实现这个功能。

qgzx9mmu

qgzx9mmu5#

我曾经有过一个用例,需要一个pre-checkout钩子(我们有开发者滥用git checkout-B和git push-force,这引起了各种各样的噩梦)。Git没有太多钩子,所以我也使用了同样的解决方案,在不修改二进制文件的情况下添加新的钩子。
注意:此解决方案要求您将/usr/bin/git重命名为/usr/bin/git.real,并将其替换为PHP脚本。因此,如果您将其设置为显示Git状态,则命令提示符的显示速度会略微降低几毫秒,例如:测试@工作虚拟机/var/www/html/一些应用程序(主)#
这些挂接对于运行命令的计算机上的TRUE "Pre"和"Post"挂接非常有用(不在远程存储库中)。如果你需要一个钩子在git命令执行期间触发,不要使用它们。你必须使用真正的钩子在git处理命令时真正地钩住它(比如git commit消息钩子,远程repo钩子等等).为什么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();
      }
   }
}

相关问题