├── .git-templates └── hooks │ └── pre-commit ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── git-identity.cpp └── json.hpp /.git-templates/hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Check git identity 4 | # 5 | if ! git identity check; then 6 | exit 1 7 | fi 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build*/ 3 | *.user 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(GitIdentityManagerCpp) 3 | 4 | set(CMAKE_CXX_STANDARD 14) 5 | 6 | add_executable(git-identity 7 | git-identity.cpp) 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Duncan Ogilvie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitIdentityManagerCpp 2 | 3 | Are you accidentally comitting with the wrong git identity? If so, this tool is for you! Always make sure your git identity is set in the local repository config. Use a global git hook to make sure you never mess up with a new repository again! 4 | 5 | Cross platform command line port of [GitIdentityManager](https://github.com/mrexodia/GitIdentityManager) (probably should have been written in Go). 6 | 7 | ```bash 8 | $ git-identity 9 | (1) John Doe 10 | commit.gpgsign true 11 | tag.gpgsign true 12 | user.signingkey 12345566 13 | (2) John Doe 14 | commit.gpgsign false 15 | tag.gpgsign false 16 | user.signingkey 17 | 18 | Your choice: 19 | ``` 20 | 21 | ## Installation 22 | 23 | Compile the project with CMake and copy/symlink the `git-identity` executable in your path. 24 | 25 | ### git-identity.json 26 | 27 | You should create a file name `~/git-identity.json` that contains your identities (or any other git config). The only required fields are `user.name` and `user.email`. 28 | 29 | ``` 30 | [ 31 | { 32 | "user.name": "John Doe", 33 | "user.email": "john.doe@personal.space", 34 | "user.signingkey": "12345566", 35 | "credential.username": "johnpersonal", 36 | "commit.gpgsign": "true", 37 | "tag.gpgsign": "true" 38 | }, 39 | { 40 | "user.name": "John Doe", 41 | "user.email": "jdoe@office.corp", 42 | "credential.username": "johnoffice", 43 | "user.signingkey": "", 44 | "commit.gpgsign": "false", 45 | "tag.gpgsign": "false" 46 | } 47 | ] 48 | ``` 49 | 50 | You can now run `git identity` to change your identity in a local repository. You can use `git identity check` in scripts to see if a local identity is set. 51 | 52 | ### Global configuration 53 | 54 | The following configuration is recommended to prevent accidents: 55 | 56 | ```bash 57 | git config --global user.useConfigOnly true 58 | git config --global --unset user.name 59 | git config --global --unset user.email 60 | ``` 61 | 62 | An alternative is to set up a global `pre-commit` hook template. A sample hook is provided (`.git-templates` directory in this repository) that checks your identity before committing. See [this post](https://santexgroup.com/blog/create-a-global-git-hook-to-check-flake8-before-each-commit/) for more details. -------------------------------------------------------------------------------- /git-identity.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "json.hpp" 10 | 11 | static std::unique_ptr exec(const std::string& cmd) 12 | { 13 | auto stream = popen(cmd.c_str(), "r"); 14 | if(!stream) 15 | return nullptr; 16 | char buffer[256]; 17 | 18 | auto data = std::make_unique(); 19 | while (!feof(stream)) 20 | if (fgets(buffer, sizeof(buffer), stream) != NULL) 21 | data->append(buffer); 22 | pclose(stream); 23 | return data; 24 | } 25 | 26 | static std::unique_ptr git(const std::string& cmd) 27 | { 28 | auto stdout = exec("git " + cmd); 29 | while(stdout && stdout->size() && (stdout->at(stdout->size() - 1) == '\r' || stdout->at(stdout->size() - 1) == '\n')) 30 | stdout->pop_back(); 31 | if(stdout->empty()) 32 | return nullptr; 33 | return stdout; 34 | } 35 | 36 | static std::unique_ptr parseSize(const std::string& str) 37 | { 38 | if(str.empty()) 39 | return nullptr; 40 | 41 | size_t result = 0; 42 | for(size_t i = 0; i < str.length(); i++) 43 | { 44 | auto ch = str[i]; 45 | if(!isdigit(ch)) 46 | return nullptr; 47 | auto n = ch - '0'; 48 | size_t nextResult = result * 10 + n; 49 | if(nextResult < result) 50 | return nullptr; 51 | result = nextResult; 52 | } 53 | return std::make_unique(result); 54 | } 55 | 56 | int main(int argc, char* argv[]) 57 | { 58 | auto gitName = git("config --local user.name"); 59 | auto gitEmail = git("config --local user.email"); 60 | 61 | if(argc > 1 && strcmp(argv[1], "check") == 0) 62 | { 63 | if(gitName && gitEmail) 64 | { 65 | fprintf(stderr, "%s <%s>\n", gitName->c_str(), gitEmail->c_str()); 66 | return EXIT_SUCCESS; 67 | } 68 | else 69 | { 70 | fprintf(stderr, "Name or email not set!\n"); 71 | return EXIT_FAILURE; 72 | } 73 | } 74 | 75 | std::string homepath = getenv("HOME"); 76 | auto jsonFilename = "git-identity.json"; 77 | std::ifstream jsonFile(homepath + "/" + jsonFilename); 78 | if(!jsonFile.is_open()) 79 | { 80 | fprintf(stderr, "Failed to open '%s'!\n", jsonFilename); 81 | return EXIT_FAILURE; 82 | } 83 | 84 | auto json = nlohmann::json::parse(jsonFile); 85 | 86 | size_t defaultChoice = 0; 87 | for(size_t i = 0; i < json.size(); i++) 88 | { 89 | auto& identity = json[i]; 90 | std::string name = identity["user.name"]; 91 | std::string email = identity["user.email"]; 92 | if(gitName && gitEmail && name == *gitName && email == *gitEmail) 93 | defaultChoice = i + 1; 94 | printf("(%zu) %s <%s>\n", i + 1, name.c_str(), email.c_str()); 95 | for(auto& el : identity.items()) 96 | { 97 | std::string key = el.key(); 98 | if(key == "user.name" || key == "user.email") 99 | continue; 100 | std::string value = el.value(); 101 | printf(" %s", key.c_str()); 102 | for(size_t i = 0; i + key.length() < 20; i++) 103 | printf(" "); 104 | printf("%s\n", value.empty() ? "" : value.c_str()); 105 | } 106 | } 107 | 108 | if(defaultChoice) 109 | printf("\nYour choice (%zu): ", defaultChoice); 110 | else 111 | printf("\nYour choice: "); 112 | 113 | std::string choiceStr; 114 | std::getline(std::cin, choiceStr); 115 | 116 | auto choice = (choiceStr.empty() && defaultChoice > 0) ? std::make_unique(defaultChoice) : parseSize(choiceStr); 117 | if(!choice || *choice == 0 || *choice > json.size()) 118 | { 119 | fprintf(stderr, "Invalid choice '%s'!\n", choiceStr.c_str()); 120 | return EXIT_FAILURE; 121 | } 122 | 123 | auto identity = json[*choice - 1]; 124 | for(auto& el : identity.items()) 125 | { 126 | std::string key = el.key(); 127 | std::string value = el.value(); 128 | git("config \"" + key + "\" \"" + value + "\""); 129 | } 130 | 131 | return EXIT_FAILURE; 132 | } 133 | --------------------------------------------------------------------------------