gpt4 book ai didi

git - 在不同的 Git 分支中正确使用 GNU Global 标签文件

转载 作者:太空狗 更新时间:2023-10-29 13:27:11 24 4
gpt4 key购买 nike

我正在使用许多不同的 Git 分支开发一个大型 C++ 项目。我们正在使用 Git Flow 模型。这意味着我会定期在 Git 分支之间切换。我在 Emacs 中使用 helm-gtags 来导航代码。目前我还没有将 GNU 全局标记文件(GTAGS、GRTAGS、GPATH)置于 Git 版本控制之下,只要我更改为在不同的分支上工作,它们就会保持不变。但这可能意味着我在标记文件中有代码中不存在的符号,反之亦然。我可以更新标签文件 (helm-gtags-update-tags),但这不会从标签文件中删除代码中不存在的符号,它只会添加或更新代码中实际存在的符号。

在由 Git 控制版本的代码中使用 GNU Global 标签文件的最佳方式是什么?是否应该将标记文件添加到 Git,以便它们特定于每个分支?或者我是否应该在每次切换分支以生成一组新标签时删除标签文件?还是有别的办法?

最佳答案

提交它们似乎不是最好的主意,并且根据 global 重新索引整个项目需要多长时间,您可能会发现每次更改分支时都重建整个数据库是不可取的。

如果完全重建花费的时间太长,这里是我编写的 post-checkout git hook,用于在每个分支的基础上管理未跟踪的文件。

对于每个分支,您只需将 GTAGS、GRTAGS、GPATH 文件复制到此脚本使用的 .branches 目录的适当命名的子目录中,然后脚本将交换文件每当您更改分支机构时。

#!/bin/sh

# Git hook (post-checkout) to manage *untracked* files on a per-branch basis.
# Author: Phil S.
# Version 1.1
# URL: http://stackoverflow.com/a/42686433

## Commentary:
#
# This hook is very useful when you have development-specific files which
# you never want to commit to the repository, but which can vary on a
# branch-by-branch basis. Branch-specific configuration files are a
# prime use-case (to specify the database to use, for instance).
#
# With this hook, those files are automatically copied into the working
# copy when you checkout the branch they are associated with, and moved
# back again when you checkout another branch.
#
# The hook uses a .branches directory in the root of the repository,
# so you should add this to your .gitignore file.
#
# To start managing files for a branch, simply copy them into a sub-
# directory (of .branches) named after the branch in question, changing
# any forward slashes in the branch name into double-underscores.


## Example:
#
# [phil/master] ~/site$ grep .branches .gitignore
# .branches
#
# [phil/master] ~/site$ find .branches/
# .branches/
# .branches/phil__master
# .branches/phil__master/sites
# .branches/phil__master/sites/default
# .branches/phil__master/sites/default/settings.branch.php
# .branches/phil__media
# .branches/phil__media/sites
# .branches/phil__media/sites/default
# .branches/phil__media/sites/default/settings.branch.php
#
# [phil/master] ~/site$ git checkout phil/media
# Switched to branch 'phil/media'
# Removing untracked per-branch files for: phil__master
# `../.././sites/default/settings.branch.php' -> `./sites/default/settings.branch.php'
# Adding untracked per-branch files for: phil__media
# >f+++++++++ sites/default/settings.branch.php


## Caveat:
#
# An early version of this script had issues whenever a git operation checked
# out a detached HEAD, such that the .branches/.current_branch file contained
# "HEAD" rather than the branch directory name, and so the intended untracked
# files were not copied.
#
# I never got caught out by this, because my prompt always shows me the
# .current_branch value (see comments at the end of the hook), so I notice
# when it says HEAD unexpectedly; however I do not recall this happening at
# all in the past few years, so I believe it is no longer a concern.
#
# If it were to happen to you, simply running git checkout (branch) for the
# branch you are already on fixes it up. The log file may also help to debug
# any such issues.
#
# n.b. It's feasible that git could update the HEAD ref without performing
# a checkout (after initially checking out a detached head), but the cases
# I've observed (and fixed) were associated with rebasing, where this script
# had (undesirably) permitted its own processing to occur after an initial
# checkout of a detached HEAD, and had then exited early (as intended when
# rebasing) after the subsequent checkout of the eventual branch. The solution
# was to improve the detection of the cases in which we wish to exit early,
# to cover the former case as well as the latter.


## Changelog:
#
# v1.1: Handle additional text following "rebase" in GIT_REFLOG_ACTION.
# Renamed $git_dir to $root (it's the working copy root, not .git)
# Log git environment vars even when aborting.


## General information on Git post-checkout hooks:
#
# This hook is invoked when a git checkout is run after having updated
# the worktree. The hook is given three parameters: the ref of the
# previous HEAD, the ref of the new HEAD (which may or may not have
# changed), and a flag indicating whether the checkout was a branch
# checkout (changing branches, flag=1) or a file checkout (retrieving
# a file from the index, flag=0). This hook cannot affect the outcome
# of git checkout.
#
# It is also run after git clone, unless the --no-checkout (-n) option
# is used. The first parameter given to the hook is the null-ref, the
# second the ref of the new HEAD and the flag is always 1.
#
# This hook can be used to perform repository validity checks,
# auto-display differences from the previous HEAD if different, or set
# working dir metadata properties.

##############################################################################

head_old=$1
head_new=$2
flag=$3

# n.b. pwd will be this working copy's root directory.
root=$(pwd)

# Debug log.
log=".branches/post-checkout.log"
echo "\n$(date)" >>${log} 2>&1
if test -f .branches/.current_branch; then
echo ".current_branch: $(cat .branches/.current_branch)" >>${log} 2>&1
else
echo ".current_branch (file missing)" >>${log} 2>&1
fi
echo "Old: $(git log --max-count=1 --decorate ${head_old} | head -1)" >>${log} 2>&1
echo "New: $(git log --max-count=1 --decorate ${head_new} | head -1)" >>${log} 2>&1

# Log the GIT environment variables. This is primarily to assist with finding
# workarounds for any edge cases that might crop up. (This is how I discovered
# GIT_REFLOG_ACTION.)
set | grep GIT >>${log} 2>&1

# Check the 'flag' parameter ($3).
if test "$flag" != "1"; then # not a branch switch.
echo "$0 aborted (not a branch switch)." 2>&1 | tee -a ${log}
echo "Check ${log} for details."
exit 0
fi

# This hook is also invoked with flag=1 when rebasing, which we never want.
# We only want to move the untracked files around when we have explictly
# requested a checkout (which also means that the .current_branch file must
# only ever be updated at those same times).
if test "${GIT_REFLOG_ACTION##rebase}" != "${GIT_REFLOG_ACTION}"; then
echo "$0 aborted (GIT_REFLOG_ACTION indicated rebase)." 2>&1 | tee -a ${log}
echo "Check ${log} for details."
exit 0
elif test -d "$root/.git/rebase-merge"; then
echo "$0 aborted (.git/rebase-merge indicated rebase)." 2>&1 | tee -a ${log}
echo "Check ${log} for details."
exit 0
fi

# Determine which .branches directory we were originally using.
# There is no pre-checkout hook, but we can include a marker file amongst
# the untracked files identifying the current branch name, and use that to
# update the versions of the files under .branches from the current versions
# before copying the new versions.
cd "$root"
if test -f .branches/.current_branch; then
oldbranch=$(cat .branches/.current_branch)
oldbranch_dir=".branches/$oldbranch"
if test -d "$oldbranch_dir"; then
echo "Removing untracked per-branch files for: $oldbranch"
cd "$oldbranch_dir"
find . -type f -print0 | xargs -0r -I{} mv -v -f ../../{} {}
fi
fi

# Establish the name of the newly checked-out branch.
cd "$root"
newbranch=$(git symbolic-ref -q HEAD)
newbranch=${newbranch##refs/heads/}
newbranch=${newbranch:-HEAD}
newbranch=$(echo $newbranch | sed 's/\//__/')
newbranch_dir=".branches/$newbranch"

# Create/update marker file.
test -d .branches || mkdir .branches
echo $newbranch >.branches/.current_branch
echo ".current_branch: $(cat .branches/.current_branch)" >>${log} 2>&1

# Copy across the untracked files needed for the new branch.
echo "Adding untracked per-branch files for: $newbranch"

if ! test -d "$newbranch_dir"; then
echo "$newbranch_dir not found; nothing to update."
exit 0
fi

cd "$newbranch_dir"
rsync -r -i . ../../

# You can also set a fancy prompt in bash to show you which branch you're in.
# (Used to be super-useful when rebasing was a problem, but probably still
# handy just for confirming that things are as they should be.)
# PS1="${debian_chroot:+($debian_chroot)}\u@ [\$(cat /var/www/(site)/.branches/.current_branch | sed 's/__/\//')] \w\$ "

# Local Variables:
# outline-regexp: "^##"
# eval: (outline-minor-mode 1)
# eval: (while (re-search-forward "^## .+:" nil t) (outline-toggle-children))
# End:

就我个人而言,我从来没有从 Ctags 切换到 Global,我使用一种相当蛮力的方法来解决使我的 TAGS 文件保持最新的一般问题,即使用计时器来运行异步 shell 命令确定是否有任何文件比 TAGS 最近被修改,如果是,则构建一个新的 TAGS 文件。如果新文件与旧文件不同,我会替换旧文件并将 Emacs 的标签完成表设置为 nil,以便下次重新读取 TAGS 文件。

因此,重建 TAGS 文件是我在当前分支中进行代码更改以及切换到另一个分支的解决方案,因此我从来没有太多理由使用我的 post-checkout Hook 来达到我的目的在这里建议。不过,根据计时器触发的时间,代码更改和 TAGS 文件同步之间可能会有几分钟的延迟,因此更直接的响应会更好。

关于git - 在不同的 Git 分支中正确使用 GNU Global 标签文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42680131/

24 4 0