Initial commit
This commit is contained in:
		
						commit
						503575bd9f
					
				
							
								
								
									
										110
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,110 @@
 | 
				
			|||||||
 | 
					# git-me
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`git me` is a very simple identity management script for the popular distributed version control system `git`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It is written in Python (3.6+) which is easy to install on Linux distributions, and often already installed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Motivations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When you have multiple online identities, it can get a bit confusing with `git` as you either have to remember to
 | 
				
			||||||
 | 
					specify your identity for every repo or use a global configuration and end up leaking the wrong identity
 | 
				
			||||||
 | 
					to the wrong repository.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					With `git-me` fully installed, you will not be able to use identity-leaking commands such as `git commit`
 | 
				
			||||||
 | 
					unless you have an identity specified **in that repository's local `.git/config` file**.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Of course, `git-me` provides a quick way to configure that identity from a list of presets that you provide,
 | 
				
			||||||
 | 
					either in a TSV file or by using `git me add`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Side-note: git and SSH for multiple accounts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(Side-note:) In order to use multiple GitLab/GitHub accounts with different SSH keys, you can use a `ssh-config` like the
 | 
				
			||||||
 | 
					following:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Host acc1.gitlab
 | 
				
			||||||
 | 
					        HostName gitlab.com
 | 
				
			||||||
 | 
					        RSAAuthentication Yes
 | 
				
			||||||
 | 
					        IdentityFile ~/.ssh/acc1_rsa
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Host o42.gitlab
 | 
				
			||||||
 | 
					        HostName gitlab.com
 | 
				
			||||||
 | 
					        RSAAuthentication Yes
 | 
				
			||||||
 | 
					        IdentityFile ~/.ssh/oliichi42_rsa
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Host gitlab.com
 | 
				
			||||||
 | 
					        HostName specify.a.gitlab.account
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`git me` is designed to be simple and intuitive. See some of the commands here:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Managing identities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Identities will be stored in `~/.config/git-me` by default, but the `GIT_ME_STORE` environment variable can
 | 
				
			||||||
 | 
					be used to change that. The identity store file is a TSV (tab-seperated value) file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### To add an identity:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`git me add <slug> "<name>" <e-mail address>`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note: a `slug` is an easy-to-type specifier that you choose; it must be unique.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
					`git me add personal "Joseph Watson" j.watson@example.com`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### To list your identities:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`git me ls` will list your identities in a (currently skewed) table.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					joe@jwpc:~$ git me ls
 | 
				
			||||||
 | 
					git-me identity store: /home/joe/.config/git-me
 | 
				
			||||||
 | 
					# Slug          Name            E-mail Address
 | 
				
			||||||
 | 
					personal                Joseph Watson           j.watson@example.com
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### To remove an identity:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`git me rm <slug>` will delete the identity with that slug.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
					`git me rm personal`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Using an identity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`git me use <slug>` will configure the current git repo to use the identity with that slug.
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					joe@jwpc:~/gitrepo$ git config -l --local
 | 
				
			||||||
 | 
					core.repositoryformatversion=0
 | 
				
			||||||
 | 
					core.filemode=true
 | 
				
			||||||
 | 
					core.bare=false
 | 
				
			||||||
 | 
					core.logallrefupdates=true
 | 
				
			||||||
 | 
					joe@jwpc:~/gitrepo$ git me use personal
 | 
				
			||||||
 | 
					git-me identity store: /home/joe/.config/git-me
 | 
				
			||||||
 | 
					joe@jwpc:~/gitrepo$ git config -l --local
 | 
				
			||||||
 | 
					core.repositoryformatversion=0
 | 
				
			||||||
 | 
					core.filemode=true
 | 
				
			||||||
 | 
					core.bare=false
 | 
				
			||||||
 | 
					core.logallrefupdates=true
 | 
				
			||||||
 | 
					user.name=Joseph Watson
 | 
				
			||||||
 | 
					user.email=j.watson@example.com
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Checking that your identity is being used
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can use the command `git me assert` to assert that you have an identity set for the local repository.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It will warn you and fail if you do not.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The real power of this command comes from using the included `git-checked.py` wrapper as it will
 | 
				
			||||||
 | 
					automatically assert that you have a set identity BEFORE you commit.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can use it by adding e.g. a bash alias.
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					alias git='python3.6 /path/to/git-checked.py'
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										10
									
								
								git-checked.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								git-checked.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					import sys, subprocess, os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def eprint(*all):
 | 
				
			||||||
 | 
					    print(*all, file=sys.stderr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if len(sys.argv) >= 2 and sys.argv[1] in ['commit', 'ci']:
 | 
				
			||||||
 | 
					    if os.system('git me assert') > 1:
 | 
				
			||||||
 | 
					        eprint("git-checked: Cancelling git action; fix git me issues first!")
 | 
				
			||||||
 | 
					        exit(1)
 | 
				
			||||||
 | 
					subprocess.run(['git'] + sys.argv[1:], stdout=sys.stdout, stderr=sys.stderr, stdin=sys.stdin, env=os.environ)
 | 
				
			||||||
							
								
								
									
										165
									
								
								git-me.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										165
									
								
								git-me.py
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,165 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python3.6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os, argparse, sys, csv, subprocess
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def eprint(*all):
 | 
				
			||||||
 | 
					    print(*all, file=sys.stderr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					STORAGE_LOC = os.environ['GIT_ME_STORE'] if 'GIT_ME_STORE' in os.environ else os.environ['HOME'] + '/.config/git-me'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					eprint("git-me identity store: " + STORAGE_LOC)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					STCOL_LEN = 3
 | 
				
			||||||
 | 
					STCOL_SLUG = 0
 | 
				
			||||||
 | 
					STCOL_NAME = 1
 | 
				
			||||||
 | 
					STCOL_EMAIL = 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def add(args):
 | 
				
			||||||
 | 
					    slug = args.slug.lower()
 | 
				
			||||||
 | 
					    name = args.name
 | 
				
			||||||
 | 
					    email = args.email
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        with open(STORAGE_LOC, 'r') as fstore:
 | 
				
			||||||
 | 
					            tsvin = csv.reader(fstore, delimiter='\t')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for row in tsvin:
 | 
				
			||||||
 | 
					                if len(row) < STCOL_LEN:
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                if row[STCOL_SLUG].lower() == slug:
 | 
				
			||||||
 | 
					                    eprint("An identity with that slug already exists!")
 | 
				
			||||||
 | 
					                    eprint(f"    Slug: {row[STCOL_SLUG]}")
 | 
				
			||||||
 | 
					                    eprint(f"    Name: {row[STCOL_NAME]}")
 | 
				
			||||||
 | 
					                    eprint(f"  E-mail: {row[STCOL_EMAIL]}")
 | 
				
			||||||
 | 
					                    return False
 | 
				
			||||||
 | 
					    except FileNotFoundError:
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # now add it
 | 
				
			||||||
 | 
					    with open(STORAGE_LOC, 'a+') as fstore:
 | 
				
			||||||
 | 
					        fstore.write(f"\n{slug}\t{name}\t{email}\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def list_idents(args):
 | 
				
			||||||
 | 
					    print("# Slug\t\tName\t\tE-mail Address")
 | 
				
			||||||
 | 
					    with open(STORAGE_LOC, 'r') as fstore:
 | 
				
			||||||
 | 
					        tsvin = csv.reader(fstore, delimiter='\t')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for row in tsvin:
 | 
				
			||||||
 | 
					            if len(row) < STCOL_LEN:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            print(f"{row[STCOL_SLUG]}\t\t{row[STCOL_NAME]}\t\t{row[STCOL_EMAIL]}")
 | 
				
			||||||
 | 
					    return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def assert_hook(args):
 | 
				
			||||||
 | 
					    result_name = subprocess.run(['git', 'config', '--local', '--get', 'user.name'], stdout=subprocess.PIPE)
 | 
				
			||||||
 | 
					    so_name = result_name.stdout.decode().strip()
 | 
				
			||||||
 | 
					    result_email = subprocess.run(['git', 'config', '--local', '--get', 'user.email'], stdout=subprocess.PIPE)
 | 
				
			||||||
 | 
					    so_email = result_email.stdout.decode().strip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if result_email.returncode + result_name.returncode > 0:
 | 
				
			||||||
 | 
					        if result_name.returncode > 0:
 | 
				
			||||||
 | 
					            eprint("git-me: No user.name specified!")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            eprint(f"git-me: user.name OK = {so_name}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if result_email.returncode > 0:
 | 
				
			||||||
 | 
					            eprint("git-me: No user.email specified!")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            eprint(f"git-me: user.email OK = {so_email}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        eprint("git-me: Either specify a config manually or use `git me use <id>`.")
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        eprint(f"git-me: OK. (user.name = {so_name}, user.email = {so_email})")
 | 
				
			||||||
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def remove(args):
 | 
				
			||||||
 | 
					    slug = args.idslug
 | 
				
			||||||
 | 
					    os.rename(STORAGE_LOC, STORAGE_LOC + "~tmp")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    with open(STORAGE_LOC + "~tmp", 'r') as tsvin, open(STORAGE_LOC, 'w') as tsvout:
 | 
				
			||||||
 | 
					        tsvin = csv.reader(tsvin, delimiter='\t')
 | 
				
			||||||
 | 
					        tsvout = csv.writer(tsvout, delimiter='\t')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for row in tsvin:
 | 
				
			||||||
 | 
					            if len(row) < STCOL_LEN:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            if row[STCOL_SLUG].lower() == slug:
 | 
				
			||||||
 | 
					                # delete; ignore
 | 
				
			||||||
 | 
					                pass
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                tsvout.writerow(row)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        os.unlink(STORAGE_LOC + "~tmp")
 | 
				
			||||||
 | 
					    return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def switch_identity(args):
 | 
				
			||||||
 | 
					    slug = args.idslug
 | 
				
			||||||
 | 
					    with open(STORAGE_LOC, 'r') as fstore:
 | 
				
			||||||
 | 
					        tsvin = csv.reader(fstore, delimiter='\t')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for row in tsvin:
 | 
				
			||||||
 | 
					            if len(row) < STCOL_LEN:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            if row[STCOL_SLUG].lower() == slug:
 | 
				
			||||||
 | 
					                proc_name = subprocess.run(["git", "config", "--local", "user.name", row[STCOL_NAME]])
 | 
				
			||||||
 | 
					                if proc_name.returncode != 0:
 | 
				
			||||||
 | 
					                    eprint("git-me: Failed to set user.name! (non-zero exit code. Are you in a git repo?)")
 | 
				
			||||||
 | 
					                    return False
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    proc_email = subprocess.run(["git", "config", "--local", "user.email", row[STCOL_EMAIL]])
 | 
				
			||||||
 | 
					                    if proc_name.returncode != 0:
 | 
				
			||||||
 | 
					                        eprint("git-me: Failed to set user.email! (non-zero exit code.)")
 | 
				
			||||||
 | 
					                        return False
 | 
				
			||||||
 | 
					                return True
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            eprint(f"git-me: Unable to find identity with {slug}")
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    parser = argparse.ArgumentParser(
 | 
				
			||||||
 | 
					        description='Manages repository identities. Environment variable GIT_ME_STORE can be used to override location of identity store.',
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    subparser = parser.add_subparsers(help='sub-command help', dest='cmd')
 | 
				
			||||||
 | 
					    subparser.required = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    parser_assert = subparser.add_parser('assert', aliases=['check', 'hook', 'ck'])
 | 
				
			||||||
 | 
					    parser_assert.set_defaults(func=assert_hook)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    parser_add = subparser.add_parser('add', aliases=['mk'])
 | 
				
			||||||
 | 
					    parser_add.add_argument('slug', type=str, help='A short, easy-to-type code to represent this id')
 | 
				
			||||||
 | 
					    parser_add.add_argument('name', type=str, help='Your display name')
 | 
				
			||||||
 | 
					    parser_add.add_argument('email', type=str, help='Your e-mail address')
 | 
				
			||||||
 | 
					    parser_add.set_defaults(func=add)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    parser_list = subparser.add_parser('list', aliases=['ls'])
 | 
				
			||||||
 | 
					    parser_list.set_defaults(func=list_idents)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    parser_remove = subparser.add_parser('remove', aliases=['rm'])
 | 
				
			||||||
 | 
					    parser_remove.add_argument('idslug', type=str, help='The slug of the identity to remove.')
 | 
				
			||||||
 | 
					    parser_remove.set_defaults(func=remove)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    parser_switch = subparser.add_parser('use', aliases=['switch', 'choose', 'enable'])
 | 
				
			||||||
 | 
					    parser_switch.add_argument('idslug', type=str, help='The slug of the identity to enable.')
 | 
				
			||||||
 | 
					    parser_switch.set_defaults(func=switch_identity)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    args = parser.parse_args(sys.argv[1:])
 | 
				
			||||||
 | 
					    if 'func' in args:
 | 
				
			||||||
 | 
					        if args.func(args):
 | 
				
			||||||
 | 
					            exit(0)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            exit(1)
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        print("git-me: Please specify a command!", file=sys.stderr)
 | 
				
			||||||
 | 
					        exit(2)
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user