Proper use of GNU global tag files in different branches of Git

I am working on a large C ++ project using many different branches of Git. We use the Git thread model. This means that I regularly switch between Git branches. I use helm-gtags in Emacs to navigate the code. At the moment, I did not host the GNU Global tag files (GTAGS, GRTAGS, GPATH) under Git, they just stay on when I switch to work in another branch. But this may mean that I have characters in tag files that are not in the code, and vice versa. I can update tag files (helm-gtags-update-tags), but this does not remove characters from tag files that are not present in the code, it only adds or updates characters that are actually in the code.

What is the best way to work with GNU Global tag files in code that is controlled by a version of Git? Should tag files be added to Git so that they are specific to each branch? Or should I delete tag files every time I switch a branch to create a new tag set? Or is there another way?

+5
source share
3 answers

Since tag files are associated with a specific version of the code, I believe you should check them out.

On the other hand, they are files that are specific for one style of working with code and, as a rule, are not applicable to the project, and therefore it can be argued that it should not be checked. constantly changing and adding a lot of noise for each fixation. The noise everyone else should watch.

Instead, I would like to consider setting up emacs so that it automatically updates its view of files automatically when they change. This is what most modern editors do. Here, a method using inotify is used . Or, and I know this is heresy, the time has come for the new editor. I switched from emacs to Atom this year.

0
source

Running them does not seem like a better idea, and depending on how long it takes to globally redefine your entire project, an unwanted rebuild of the entire database may occur every time you change branches.

If a complete rebuild takes too much time, stick a post-checkout git hook that I wrote to manage non-playable files based on each branch.

For each branch, you simply copy the GTAGS, GRTAGS, GPATH files to a subdirectory with the corresponding name in the .branches directory used by this script, and then the script will change the files every time you change 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. # # nb It 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 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 # nb pwd will be this working copy 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: 

Personally, I never made the transition from Ctags to global, and I use pretty brute force for the general problem of keeping my TAGS file up to date, which is to use a timer to run an asynchronous shell command to determine if any file has been changed recently than TAGS, and if so, create a new TAGS file. If the new file is different from the old file, I replace the old one and set the Emacs tag completion table to zero so that next time it re-reads the TAGS file.

Thus, restoring the TAGS file is my solution both to change the code in the current branch, and to switch to another branch, so I never had a great reason to use my post-control hook for the purposes I suggested here. Depending on when the timer fires, there may be a delay of several minutes between code changes and the TAGS file, however, a faster response will be more pleasant.

0
source

This is what I did at the end. I started adding tag files to git. But it turned out not to work very well with branching and merging, and in the end the tag files would not contain all the characters in the current branch and nothing more. Phils answer made me check git hooks. I deleted the tag files from the git version and created a hook after checking, which silently deleted all the tag files present when changing branches. When helm-gtags in Emacs detects that there are no tag files, it offers to generate them. For my project, which is quite large, it will not take much time. helm-gtags also detects when a file has been modified, and automatically updates tag files with just those changes. So now I have a system that automatically updates the tag files with all the characters in the current branch. So far it works well.

0
source

Source: https://habr.com/ru/post/1265172/


All Articles