#StackBounty: #git #grep #consult How to exclude certain directories/files from `consult-git-grep` search?

Bounty: 50

I want to exlude certain files such as .gitignore in consult-git-grep search.

  • consult-git-grep works find with following defualt setup:

(setq consult-git-grep-args "git --no-pager grep -F -n --no-color -I -e '%s')

I have updated consult-git-grep-args definition where -- '(exclude).gitignore' is added at the end, but it crashes. How could I make it work?

init.el

(require 'consult)

Followed by:

(setq consult-git-grep-args "git --no-pager grep -F -n --no-color -I -e '%s' -- '(exclude).gitignore'")


Related: How to exclude certain directories/files from counsel-git-grep search?

consult-git-grep already ignores files you put in .gitignore. In
order to exclude additional files you can write on the
consult-git-grep prompt:

#foo -- :!*.org #foo -- :!.gitignore This will search for foo and
ignore *.org files or the file .gitignore. If you want to hard code
these settings adjust the variable consult-git-grep-args, which is
similar to counsel-git-grep-cmd-default.


Get this bounty!!!

#StackBounty: #git #gnupg #digital-signature #gpg-agent gpg: skipped "12345689": Unusable secret key / how to use signing sub…

Bounty: 100

As you see, I have a key with id 12345689, and I’m trying to sign with it in git

gpg -k
/home/lz/.gnupg/pubring.kbx
---------------------------
pub   rsa4096 2020-03-02 [C]
      12345689
uid           [ unknown] Person Person <me@person.com>
sub   rsa4096 2020-03-02 [S] [expires: 2024-03-01]
sub   rsa4096 2020-03-02 [E] [expires: 2024-03-01]
sub   rsa4096 2020-03-02 [A] [expires: 2024-03-01]

But git considers this key ID unusable:

gpg2 --status-fd=2 -bsau 12345689 1
[GNUPG:] KEY_CONSIDERED 12345689 1
gpg: skipped "12345689": Unusable secret key
[GNUPG:] INV_SGNR 9 12345689
[GNUPG:] FAILURE sign 54
gpg: signing failed: Unusable secret key

as I researched, it’s because it contains [C] which is not for signing. I think I should use the subkey with [S] but how do I use it? I don’t know how to get its ID. gpg -k does not show their IDs


Get this bounty!!!

#StackBounty: #windows #powershell #git #windows-terminal How to achieve the exact powerlevel10k git prompt style in oh-my-posh?

Bounty: 100

I am very fond of the powerlevel10k git prompt with all its symbols. You can find the full list here

I have to use a windows machine for my job and WSL is not an option since I need to manage the versions of SDKs in my windows OS, so I am currently using oh-my-posh. I am using the powerlevel10k theme so it looks right for the most part. However, I can’t seem to configure the git prompt to look the same, no matter how I fiddle with the git module settings or posh-git.

So my question is: Is it possible to replicate the powerlevel10k git prompt style in oh-my-posh. If so, how?


Get this bounty!!!

#StackBounty: #git #checkout PyDriller – "Reference is not a tree"

Bounty: 50

I am trying to traverse through the commits of a repository.

I am using PyDriller to do so. I initialise the repository as a PyDriller Git repo. I then checkout the PyDriller Git repo at each commit. It works for multiple commits until I try to checkout a merge commit. I receive this error

    git.exc.GitCommandError: Cmd('git') failed due to: exit code(128)
  cmdline: git checkout -f c9e7ca3de6d9e99cfcc1439a877465e566f452b1
  stderr: 'fatal: reference is not a tree: c9e7ca3de6d9e99cfcc1439a877465e566f452b1'

I can manually checkout this commit using the terminal so I am unsure why it does not work using my python code

The code works like this:

for commit in Repository(repo_path).traverse_commits():
    ...
    ...
    current_repo.checkout(commit_hash)

Which is confusing because this works until we hit a merge commit?


Get this bounty!!!

#StackBounty: #zsh #git #diff git diff: create custom function/macro to ignore comments

Bounty: 150

I have following function in zsh, which basically works as diff, but it ignores comments (lines starting with # or empty lines) when comparing files:

function cdiff() {
  local args=("${(M)@:#-*}")
  local files=("${@:#-*}")
  /usr/bin/diff -u $args <(grep -E -v '^(#|$)' $files[1]) <(grep -E -v '^(#|$)' $files[2])
}

I would like to have something similar for git diff, if possible having my own custom function/macro cdiff and use it as git cdiff.

When I use git cdiff, I would like to see only "real" changes, while ignoring comments and empty lines.


Get this bounty!!!

#StackBounty: #git #github #verification Google cannot locate my GitHub repositories – Google Site Verification fails

Bounty: 50

I created a GitHub site last September using a Windows 10 laptop. Since then I have created five repositories. Google has never been able to locate any of them. A week ago I created a GitHub Page for the main repo but Google has not been able to locate it yet either. Microsoft Edge/Bing can locate the repository.

I provided links to the main repo and its GitHub Page in this post. Then harrymc suggested in a comment that I change the name of the repo. I have done that.

The original links (which might not work anymore) were:

https://github.com/IcterusGalbula/Tutorial_for_Running_a_Large_Number_of_R_files_using_Amazon_Web_Services

https://icterusgalbula.github.io/Tutorial_for_Running_a_Large_Number_of_R_files_using_Amazon_Web_Services

Today I recreated the repo and GitHub Page after harrymc’s comment to:

https://github.com/IcterusGalbula/RAWStutorial

https://icterusgalbula.github.io/RAWStutorial/

and renamed the original repo and GitHub Page to:

https://github.com/IcterusGalbula/Tutorial-for-Running-a-Large-Number-of-R-files-using-Amazon-Web-Services

https://icterusgalbula.github.io/Tutorial-for-Running-a-Large-Number-of-R-files-using-Amazon-Web-Services/

For months I tried adding a Google Site Verification file
downloaded from Google Search Console and pushed to the main repo of interest.

https://search.google.com/search-console/welcome

Mostly I tried using the original url for the main branch of the GitHub tutorial repo in Google Search Console (under URL prefix). The original link, which might not work anymore after following harrymc’s suggestion today, was:

https://github.com/IcterusGalbula/Tutorial_for_Running_a_Large_Number_of_R_files_using_Amazon_Web_Services/tree/main

The new URL’s as of this morning are:

https://github.com/IcterusGalbula/Tutorial-for-Running-a-Large-Number-of-R-files-using-Amazon-Web-Services/tree/main

and

https://github.com/IcterusGalbula/RAWStutorial/tree/main

Google Search Console returned this error message when I clicked the Verify button after uploading the verification file to the main branch of my original GitHub tutorial repo. I have not tried this process after creating the new repo with the shorter name and renaming the original repo this morning:

Verification method:
HTML file

Failure reason:
Your verification file has the wrong content. Are you using the verification file that you downloaded here?

Please fix your implementation and reverify, or use another verification method.

The content of the Google Site Verification file is:

 google-site-verification: google229f9162496d16ab.html

I have done everything I can think of to resolve this issue including working through ~15 hours of video in multiple Git and Github courses on Udemy, watching YouTube videos, searching the internet and adding a link to my GitHub repository multiple places on the internet including my Stack Exchange profile.

Answers to this StackOverflow post seem to be to try using Google Search Console and the question has been closed. That approach is not working for me as described above:

https://stackoverflow.com/questions/26199705/github-repository-not-listing-in-google-search

I also have looked at a similar post on GitHub Community:

https://github.community/t/cant-see-my-github-page-on-google/10947

and asked for help myself on that forum.

Thank you for any suggestions and assistance 1.) enabling Google to find and list my GitHub repos and/or my GitHub Page in search results and 2.) verifying my GitHub repository.


Get this bounty!!!

#StackBounty: #git #push #github-actions #pull-request #conventional-commits How to run commitlint in GitHub workflow on every commit o…

Bounty: 100

I created a Github repository, installed commitlint and husky locally and would like to setup a workflow running commitlint on every commit of a push when validating pull requests.

I started with this workflow

name: Run commitlint on pull request

on: pull_request

jobs:
  run-commitlint-on-pull-request:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 0

      - name: Setup Node
        uses: actions/setup-node@v2
        with:
          node-version: 14.x

      - name: Install dependencies
        run: npm install

      - name: Validate all commits from PR
        run: |
          for commit in $(git rev-list ${{ github.base_ref }}..${{ github.head_ref }}); do
              npx commitlint --from $commit --to HEAD --verbose
          done

After that I created a new branch with 3 commit messages

  • invalid one
  • feat: valid two
  • invalid three

I would expect that commit message 1 and 3 are invalid so the workflow rejects the pull request. Unfortunately the validation passes.

enter image description here

I also tried to run it without a loop

run: npx commitlint --from ${{ github.base_ref }} --to ${{ github.head_ref }} --verbose

This time the workflow didn’t pass but the output is wrong.

enter image description here

Would someone mind helping me how to fix the last workflow step to validate each commit message?


Get this bounty!!!

#StackBounty: #mysql #snapshot #git Mergeable mysql database snapshots?

Bounty: 100

I need to upgrade a WordPress site – meaning the WordPress code itself, but maybe more importantly, the plugins and themes in use.

I’ve created a git repo with a branch for each of the plugins I’m updating, and I’ve managed to capture the files that change upon upgrading each plugin. I still need to do the equivalent for any db changes that are occurring.

So the most obvious solution is something like:

  • Store the original database (a simple mysqldump wordpress > wordpress.sql for example) in the master branch.
  • In each plugin branch, create a mysqldump wordpress > wordpress_myPlugin.sql.

There are multiple problems with this, primarily that I can’t see how multiple sql dumps can be overlapped/merged to create one coherent master sql to restore from, that contains all of the changes across all of the plugin updates.

It also seems like separating the data from the schema will be important here: perhaps a full dump in master, schema-only changes for each plugin, and then finding a way to merge those schema changes into a new branch might work, and merge all of that on top of the master. Point-in-time recovery may be a way to accomplish that.

Another option I guess would be to use MySQL’s built in binary snapshots, but the whole point in doing these upgrades using git was to do it non-linearly so that any given upgrade can be easily cherry-picked and rolled back. The binary log file system seems linear in nature (unless I misunderstand it), so it’s difficult to selectively roll back one upgrade and not the others, if the one being rolled back is chronologically earlier than ones that should be kept.

This is likely not an ongoing problem but a one-time upgrade. Any help would be appreciated.
I have read Is backing up a MySQL database in git a good idea and Backup your Database in Git.


Get this bounty!!!

#StackBounty: #python #python-3.x #console #git git-user – Working on a shared local repository with multiple users made easier

Bounty: 50

I have recently been in a situation where multiple developers worked on a shared local git repository under the same Linux user┬╣. As one can imagine, it can easily become a bit annoying not to commit changes as someone else if you don’t check the values of git config user.name and git config user.email carefully and then change them accordingly. The same problem might also arise if you happen to work on several projects on your local machine side-by-side where you have to use different "identities", e.g. some work-related and private projects. I decided to tackle this with a little "git extension" that allows you to view and change the committer identity with less of a hassle.

Enter git user. To get an idea of its intended use, have a look at the output of git user -h:

git-user - Helps you to manage multiple users in a shared local repository

Subcommands
-----------
add, update, show, delete

Use git user <subcommand> --help to get more help

Examples
--------
# Add two test users
> git user add itsme "My Last" my.last@domain.tld --credential-username itsme
> git user add itsyou "Your Last" your.last@domain.tld

# activate the first user
> git user itsme
My Last <my.last@domain.tld>
# you can check this yourself with git config.name and git config user.email

# activate the second user
> git user itsyou
Your Last <your.last@domain.tld>

# use git user with no arguments to check which values are currently set
> git user
Your Last <your.last@domain.tld>

> git user show
    itsme: My Last <my.last@domain.tld> (push as 'itsme')
    itsyou: Your Last <your.last@domain.tld>
> git user delete itsyou --force
> git user show
    itsme: My Last <my.last@domain.tld>

The code that makes this possible is as follows:

#!/usr/bin/env python3
"""
... omitted for brevity, see help text in question ...
"""
import argparse
import json
import os
import sys
from collections import defaultdict
from subprocess import check_output, CalledProcessError, DEVNULL

DESCRIPTION = sys.modules[__name__].__doc__

__version__ = "0.2.0"


class UserbaseError(Exception):
    """Base class for userbase related exceptions"""


class UserDoesNotExist(UserbaseError):
    """Custom exception to indicate that a user does not exist"""


class UserDoesAlreadyExist(UserbaseError):
    """Custom exception to indicate that a user does already exist"""


class Userbase:
    """Abstraction of the underlying user storage file"""

    DATA_KEYS = ("name", "email", "credential_username")

    def __init__(self, users_file):
        self._users_file = users_file
        self._users = defaultdict(lambda: defaultdict(str))
        self._load()

    def _load(self):
        if not os.path.isfile(self._users_file):
            print("Creating default at '{}'.".format(self._users_file))
            os.makedirs(os.path.dirname(self._users_file))
            self.save()

        with open(self._users_file, "r") as json_file:
            self._users.update(json.load(json_file))

    def save(self):
        """Dump the current userbase to file"""
        with open(self._users_file, "w") as json_file:
            json.dump(self._users, json_file)

    def is_known(self, alias):
        """Check if an alias is part of the userbase"""
        return alias in self._users

    def get(self, alias):
        """Try to get user data for an alias

        Parameters
        ----------
        alias: str
            access the data stored under this alias

        Returns
        -------
        dict
            the data associated with the alias. Keys are listed in
            Userbase.DATA_KEYS

        Raises
        ------
        UserDoesNotExist
            If the user is not part of the userbase
        """
        if self.is_known(alias):
            return self._users[alias]

        raise UserDoesNotExist(
            "User with alias '{}' unknown".format(alias)
        )

    def as_dict(self):
        """Access the userbase as a dict"""
        return dict(self._users)

    def update(self, alias, **kwargs):
        """Update the data stored under an alias

        This function does not check whether the user exists or not. If it
        exists, its data will simply be overwritten.

        Parameters
        ----------
        alias: str
            update the data found under this alias
        kwargs: dict
            the script will look for the keys from Userbase.DATA_KEYS in the
            kwargs in order to update the internal database
        """
        for key in Userbase.DATA_KEYS:
            new_value = kwargs[key]
            if new_value is not None:
                self._users[alias][key] = new_value

    def delete(self, alias):
        """Delete a user from the userbase given its alias

        Parameters
        ----------
        alias: str
            the alias to look for

        Raises
        ------
        UserDoesNotExist
            you can probably guess when this is raised
        """
        try:
            del self._users[alias]
        except KeyError:
            raise UserDoesNotExist(
                "User with alias '{}' unknown".format(alias)
            )


try:
    _USERS_FILE = os.environ["GITUSER_CONFIG"]
except KeyError:
    _USERS_FILE = os.path.join(
        os.path.expanduser("~"), ".config", "git-user", "users.json"
    )
_USERS_FILE = os.path.abspath(_USERS_FILE)
_USERS = Userbase(_USERS_FILE)


def add(args):
    """Add a user to the userbase"""
    if _USERS.is_known(args.alias):
        raise UserDoesAlreadyExist(
            "User with alias '{}' already exist. ".format(args.alias)
            + "Delete first or use 'update'"
        )
    kwargs = {name: getattr(args, name, "") for name in Userbase.DATA_KEYS}
    _USERS.update(args.alias, **kwargs)


def update(args):
    """Interactive wrapper around Userbase.update"""
    if not _USERS.is_known(args.alias):
        raise UserDoesAlreadyExist(
            "User with alias '{}' does not exist. ".format(args.alias)
            + "Add first using 'add'"
        )
    kwargs = {name: getattr(args, name, "") for name in Userbase.DATA_KEYS}
    _USERS.update(args.alias, **kwargs)


def delete(args):
    """Interactivate wrapper around Userbase.delete"""
    if _USERS.is_known(args.alias) and not args.force:
        while True:
            answer = input(
                "Really delete user '{}'? [N/y] ".format(args.alias)
            )
            answer = answer.lower().strip()
            if answer in ("yes", "y"):
                break
            if answer in ("no", "n", ""):
                return
    _USERS.delete(args.alias)


def show(args):
    """Show the data of one or all the users in the userbase"""
    to_show = tuple(sorted(_USERS.as_dict().keys()))
    if args.alias is not None:
        to_show = (args.alias, )

    if to_show:
        for alias in to_show:
            cfg = _USERS.get(alias)
            msg = "    {}: {name} <{email}>".format(alias, **cfg)
            if "credential_username" in cfg.keys():
                msg += " (push as '{credential_username}')".format(**cfg)
            print(msg)
    else:
        print("No known aliases.")


def switch(args):
    """Switch to an other user from the userbase and/or show current config

    Set args.quiet to True to avoid seeing the current config as console output
    """
    if args.alias is not None:
        cfg = _USERS.get(args.alias)
        _git_config_name(cfg["name"])
        _git_config_email(cfg["email"])
        credential_username = cfg.get("credential_username", "")
        try:
            _git_config_credential_username(credential_username)
        except CalledProcessError as ex:
            if credential_username not in ("", None):
                raise ex
    if not args.quiet:
        _show_git_config()


def _show_git_config():
    try:
        git_name = _git_config_name().strip().decode("utf8")
        git_email = _git_config_email().strip().decode("utf8")
    except CalledProcessError:
        print(
            "Currently there is no (default) user for this repository.n"
            "Select one using git user <alias> or manually with git config"
        )
        return
    try:
        git_cred_username = _git_config_credential_username().strip().decode("utf8")
        print("{} <{}> (push as '{}')".format(git_name, git_email, git_cred_username))
        return
    except CalledProcessError:
        # git config has a non-zero exit status if no value is set
        pass

    print("{} <{}>".format(git_name, git_email))


def _git_config_name(name=None):
    args = ["git", "config", "user.name"]
    if name is not None:
        args.append(str(name))
    return check_output(args)


def _git_config_email(email=None):
    args = ["git", "config", "user.email"]
    if email is not None:
        args.append(str(email))
    return check_output(args)


def _git_config_credential_username(username=None):
    # remote_url = _git_get_remote_url(remote).strip().decode("utf8")
    args = ["git", "config"]
    if username == "":
        args.extend(["--remove-section", "credential"])
    else:
        args.append("credential.username")
        if username is not None:
            args.append(username)
    return check_output(args, stderr=DEVNULL)


def main():
    """CLI of the git user helper"""
    parser = argparse.ArgumentParser(
        description=DESCRIPTION, formatter_class=argparse.RawTextHelpFormatter)

    # click might be an alternative here, but I want this to be as lightweight
    # as possible
    try:
        command = sys.argv[1]
    except IndexError:
        command = "switch"

    if command in ("add", "update", "delete", "show"):
        subparsers = parser.add_subparsers()

        add_subparser = subparsers.add_parser(
            "add", description="Add name and email for an alias")
        add_subparser.add_argument(
            "alias", help="Alias used to access this user's name and email")
        add_subparser.add_argument(
            "name", help="This value gets passed to git config user.name",
            default="")
        add_subparser.add_argument(
            "email", help="This value gets passed to git config user.email",
            default="")
        add_subparser.add_argument(
            "--credential-username",
            help="optional: push credential username",
            default="")
        add_subparser.set_defaults(command=add)

        update_subparser = subparsers.add_parser(
            "update", description="Update an alias")
        update_subparser.add_argument(
            "alias", help="Alias used to access this user's name and email")
        update_subparser.add_argument(
            "--name", help="This value gets passed to git config user.name",
            default=None)
        update_subparser.add_argument(
            "--email", help="This value gets passed to git config user.email",
            default=None)
        update_subparser.add_argument(
            "--credential-username",
            help="optional: push credential username",
            default=None)
        update_subparser.set_defaults(command=update)

        delete_subparser = subparsers.add_parser(
            "delete", description="Delete name and email stored for an alias")
        delete_subparser.add_argument(
            "alias", help="Delete name, email and possibly credentials for this alias")
        delete_subparser.add_argument(
            "--force", action="store_true", help="Delete without interaction"
        )
        delete_subparser.set_defaults(command=delete)

        show_subparser = subparsers.add_parser(
            "show",
            description="Show name and email associated with this alias")
        show_subparser.add_argument("alias", nargs="?", default=None)
        show_subparser.set_defaults(command=show)
    else:
        parser.add_argument(
            "alias",
            nargs="?",
            default=None,
            help="Use git config with name and email that belong to this alias")
        parser.add_argument(
            "--quiet", action="store_true",
            help="Suppress confirmation output after setting the config"
        )
        parser.add_argument(
            "--version", action="store_true",
            help="show git and git-user version and exit"
        )
        parser.set_defaults(command=switch)

    args = parser.parse_args()
    if getattr(args, "version", False):
        print("git-user version {}".format(__version__))
        return

    try:
        args.command(args)
    except (UserbaseError, CalledProcessError) as err:
        print(err)
        sys.exit(1)

    # only save on proper exit
    _USERS.save()


if __name__ == "__main__":
    main()

To test it, copy and paste the file somewhere in your $PATH where git can pick it up, name it git-user and make it executable. A symlink with this name is also possible if you prefer to have the file with a .py extension.

Notes to kind reviewers

  • Every kind of feedback is welcome. I’m also especially interested, how the script worked for you regarding usability. Was the help text actually, well, helpful?
  • As one of the comments tells, I knowingly ignored possibly useful packages like click in order to allow this to run on systems with no additional python packages.
  • Support for signature keys that would make it easier to sign your commits as well is on the TODO list, but not implemented yet.
  • The code was checked with pylint and pycodestyle, so it should be in a reasonable shape regarding code style.

┬╣ Whether or not this is a good idea might be arguable, but that’s not the point here.


Get this bounty!!!