Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
G
GPM - NTNU Git Profile Manager
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Leo
GPM - NTNU Git Profile Manager
Commits
f9750937
Commit
f9750937
authored
10 months ago
by
Leo
Browse files
Options
Downloads
Patches
Plain Diff
chore(comments): Add better docs
parent
5bd42058
No related branches found
No related tags found
No related merge requests found
Pipeline
#291438
passed
10 months ago
Stage: build
Stage: deploy
Changes
2
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
.idea/workspace.xml
+3
-2
3 additions, 2 deletions
.idea/workspace.xml
gpm/cli.py
+122
-4
122 additions, 4 deletions
gpm/cli.py
with
125 additions
and
6 deletions
.idea/workspace.xml
+
3
−
2
View file @
f9750937
...
@@ -5,7 +5,8 @@
...
@@ -5,7 +5,8 @@
</component>
</component>
<component
name=
"ChangeListManager"
>
<component
name=
"ChangeListManager"
>
<list
default=
"true"
id=
"2f47370d-26d1-4fc6-a93f-363c8b4c6e5a"
name=
"Changes"
comment=
""
>
<list
default=
"true"
id=
"2f47370d-26d1-4fc6-a93f-363c8b4c6e5a"
name=
"Changes"
comment=
""
>
<change
beforePath=
"$PROJECT_DIR$/.gitlab-ci.yml"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/.gitlab-ci.yml"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/.idea/workspace.xml"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/.idea/workspace.xml"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/gpm/cli.py"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/gpm/cli.py"
afterDir=
"false"
/>
</list>
</list>
<option
name=
"SHOW_DIALOG"
value=
"false"
/>
<option
name=
"SHOW_DIALOG"
value=
"false"
/>
<option
name=
"HIGHLIGHT_CONFLICTS"
value=
"true"
/>
<option
name=
"HIGHLIGHT_CONFLICTS"
value=
"true"
/>
...
@@ -83,7 +84,7 @@
...
@@ -83,7 +84,7 @@
<option
name=
"presentableId"
value=
"Default"
/>
<option
name=
"presentableId"
value=
"Default"
/>
<updated>
1724140762915
</updated>
<updated>
1724140762915
</updated>
<workItem
from=
"1724140765116"
duration=
"10726000"
/>
<workItem
from=
"1724140765116"
duration=
"10726000"
/>
<workItem
from=
"1724224393281"
duration=
"8
004
000"
/>
<workItem
from=
"1724224393281"
duration=
"8
838
000"
/>
</task>
</task>
<servers
/>
<servers
/>
</component>
</component>
...
...
This diff is collapsed.
Click to expand it.
gpm/cli.py
+
122
−
4
View file @
f9750937
...
@@ -53,6 +53,10 @@ def find_best_match(query, options):
...
@@ -53,6 +53,10 @@ def find_best_match(query, options):
def
setup
():
def
setup
():
"""
Setup the CLI parser, handle argument- and subcommand parsing.
:return:
"""
if
not
os
.
path
.
exists
(
data_location
):
if
not
os
.
path
.
exists
(
data_location
):
os
.
makedirs
(
data_location
)
os
.
makedirs
(
data_location
)
if
not
os
.
path
.
exists
(
data_file
):
if
not
os
.
path
.
exists
(
data_file
):
...
@@ -121,6 +125,10 @@ def setup():
...
@@ -121,6 +125,10 @@ def setup():
@dataclass
@dataclass
class
Profile
:
class
Profile
:
"""
Dataclass for a git profile.
Represents a git profile with a name, email and optional comment.
"""
name
:
str
name
:
str
email
:
str
email
:
str
comment
:
Optional
[
str
]
=
None
comment
:
Optional
[
str
]
=
None
...
@@ -135,12 +143,23 @@ class Profile:
...
@@ -135,12 +143,23 @@ class Profile:
class
GPM
:
class
GPM
:
"""
Git Profile Manager class. Otherwise known as the Main Program
"""
def
__init__
(
self
,
use_global_git_config
=
False
):
def
__init__
(
self
,
use_global_git_config
=
False
):
"""
Initialize the Git Profile Manager
:param use_global_git_config: Whether to use the global git config or not, when initializing the class
"""
self
.
profiles
=
[]
self
.
profiles
=
[]
self
.
_load
()
self
.
_load
()
# Load profiles from file `data_file`
self
.
use_global_git_config
=
use_global_git_config
self
.
use_global_git_config
=
use_global_git_config
def
_load
(
self
):
def
_load
(
self
):
"""
Load profiles from the data file
:return: List of profiles
"""
with
open
(
data_file
,
"
r
"
)
as
f
:
with
open
(
data_file
,
"
r
"
)
as
f
:
data
=
json
.
load
(
f
)
data
=
json
.
load
(
f
)
try
:
try
:
...
@@ -149,11 +168,22 @@ class GPM:
...
@@ -149,11 +168,22 @@ class GPM:
print
(
f
"
{
colorama
.
Fore
.
RED
}
Error loading profiles
{
colorama
.
Style
.
RESET_ALL
}
"
)
print
(
f
"
{
colorama
.
Fore
.
RED
}
Error loading profiles
{
colorama
.
Style
.
RESET_ALL
}
"
)
sys
.
exit
(
1
)
sys
.
exit
(
1
)
def
_save
(
self
):
def
_save
(
self
)
->
None
:
"""
Save the profiles to the data file
:return: None
"""
with
open
(
data_file
,
"
w
"
)
as
f
:
with
open
(
data_file
,
"
w
"
)
as
f
:
json
.
dump
([
profile
.
__dict__
for
profile
in
self
.
profiles
],
f
,
indent
=
4
)
json
.
dump
([
profile
.
__dict__
for
profile
in
self
.
profiles
],
f
,
indent
=
4
)
def
add
(
self
,
name
,
email
,
comment
=
None
):
def
add
(
self
,
name
,
email
,
comment
=
None
):
"""
Add a new profile to the list of profiles
:param name: The name of the profile
:param email: The email of the profile
:param comment: Optional comment for the profile
:return: None
"""
# Validate name and email
# Validate name and email
if
not
re
.
match
(
r
"
[^@]+@[^@]+\.[^@]+
"
,
email
):
if
not
re
.
match
(
r
"
[^@]+@[^@]+\.[^@]+
"
,
email
):
print
(
f
"
{
colorama
.
Fore
.
RED
}
Invalid email
{
colorama
.
Style
.
RESET_ALL
}
:
{
email
}
"
)
print
(
f
"
{
colorama
.
Fore
.
RED
}
Invalid email
{
colorama
.
Style
.
RESET_ALL
}
:
{
email
}
"
)
...
@@ -161,6 +191,8 @@ class GPM:
...
@@ -161,6 +191,8 @@ class GPM:
if
not
name
:
if
not
name
:
print
(
f
"
{
colorama
.
Fore
.
RED
}
Name cannot be empty
{
colorama
.
Style
.
RESET_ALL
}
"
)
print
(
f
"
{
colorama
.
Fore
.
RED
}
Name cannot be empty
{
colorama
.
Style
.
RESET_ALL
}
"
)
return
return
# Check if profile already exists
for
profile
in
self
.
profiles
:
for
profile
in
self
.
profiles
:
if
profile
.
email
==
email
:
if
profile
.
email
==
email
:
print
(
f
"
{
colorama
.
Fore
.
RED
}
Email
{
email
}
already exists
{
colorama
.
Style
.
RESET_ALL
}
"
)
print
(
f
"
{
colorama
.
Fore
.
RED
}
Email
{
email
}
already exists
{
colorama
.
Style
.
RESET_ALL
}
"
)
...
@@ -168,10 +200,17 @@ class GPM:
...
@@ -168,10 +200,17 @@ class GPM:
profile
=
Profile
(
name
,
email
,
comment
)
profile
=
Profile
(
name
,
email
,
comment
)
self
.
profiles
.
append
(
profile
)
self
.
profiles
.
append
(
profile
)
self
.
_save
()
self
.
_save
()
#print(f"Added profile: {name} <{email}>")
print
(
f
"
{
colorama
.
Fore
.
GREEN
}
Added profile:
{
self
.
get_profile_format
(
profile
)
}
"
)
print
(
f
"
{
colorama
.
Fore
.
GREEN
}
Added profile:
{
self
.
get_profile_format
(
profile
)
}
"
)
def
edit
(
self
,
query
,
name
=
None
,
email
=
None
,
comment
=
None
):
def
edit
(
self
,
query
,
name
=
None
,
email
=
None
,
comment
=
None
):
"""
Edit an existing profile
:param query: The query to match the profile to edit
:param name: If provided, the new name for the profile
:param email: If provided, the new email for the profile
:param comment: If provided, the new comment for the profile
:return:
"""
if
not
name
and
not
email
and
not
comment
:
if
not
name
and
not
email
and
not
comment
:
print
(
f
"
{
colorama
.
Fore
.
RED
}
Nothing to edit
{
colorama
.
Style
.
RESET_ALL
}
"
)
print
(
f
"
{
colorama
.
Fore
.
RED
}
Nothing to edit
{
colorama
.
Style
.
RESET_ALL
}
"
)
return
return
...
@@ -188,6 +227,10 @@ class GPM:
...
@@ -188,6 +227,10 @@ class GPM:
print
(
f
"
{
colorama
.
Fore
.
GREEN
}
Edited profile:
{
self
.
get_profile_format
(
profile
)
}{
colorama
.
Style
.
RESET_ALL
}
"
)
print
(
f
"
{
colorama
.
Fore
.
GREEN
}
Edited profile:
{
self
.
get_profile_format
(
profile
)
}{
colorama
.
Style
.
RESET_ALL
}
"
)
def
list
(
self
):
def
list
(
self
):
"""
List all profiles
:return: None
"""
if
not
self
.
profiles
:
if
not
self
.
profiles
:
print
(
f
"
{
colorama
.
Fore
.
YELLOW
}
No profiles found
{
colorama
.
Style
.
RESET_ALL
}
"
)
print
(
f
"
{
colorama
.
Fore
.
YELLOW
}
No profiles found
{
colorama
.
Style
.
RESET_ALL
}
"
)
else
:
else
:
...
@@ -195,7 +238,12 @@ class GPM:
...
@@ -195,7 +238,12 @@ class GPM:
print
(
f
"
{
index
}
.
{
profile
}
"
)
print
(
f
"
{
index
}
.
{
profile
}
"
)
def
match_query_to_profile
(
self
,
query
:
str
):
def
match_query_to_profile
(
self
,
query
:
str
)
->
Optional
[
Profile
]:
"""
Match a query to a profile
:param query: The query to match
:return: The profile if found, otherwise None
"""
if
query
.
isdigit
():
if
query
.
isdigit
():
index
=
int
(
query
)
-
1
index
=
int
(
query
)
-
1
if
index
<
0
or
index
>=
len
(
self
.
profiles
):
if
index
<
0
or
index
>=
len
(
self
.
profiles
):
...
@@ -222,15 +270,30 @@ class GPM:
...
@@ -222,15 +270,30 @@ class GPM:
return
profile
return
profile
def
get_profile_format
(
self
,
profile
:
Profile
):
def
get_profile_format
(
self
,
profile
:
Profile
):
"""
Get the formatted profile string
:param profile:
:return: The formatted profile string in format (index) name (comment) <email>
"""
return
f
"
(
{
self
.
get_profile_number
(
profile
)
}
)
{
profile
}
"
return
f
"
(
{
self
.
get_profile_number
(
profile
)
}
)
{
profile
}
"
def
_get_git_subcommand
(
self
,
*
params
):
def
_get_git_subcommand
(
self
,
*
params
):
"""
Get the git subcommand for a given set of parameters
:param params: The parameters to pass to the git command
:return: The git command string
"""
git_command
=
"
git config
"
git_command
=
"
git config
"
if
self
.
use_global_git_config
:
if
self
.
use_global_git_config
:
git_command
+=
"
--global
"
git_command
+=
"
--global
"
return
f
"
{
git_command
}
{
'
'
.
join
(
params
)
}
"
return
f
"
{
git_command
}
{
'
'
.
join
(
params
)
}
"
def
use
(
self
,
name_or_email
):
def
use
(
self
,
name_or_email
):
"""
Use a profile, by applying the name and email to the git config
:param name_or_email: The name or email of the profile to use
:return: None
"""
if
profile
:
=
self
.
match_query_to_profile
(
name_or_email
):
if
profile
:
=
self
.
match_query_to_profile
(
name_or_email
):
print
(
f
"
Using profile
{
self
.
get_profile_format
(
profile
)
}
"
,
end
=
"
"
)
print
(
f
"
Using profile
{
self
.
get_profile_format
(
profile
)
}
"
,
end
=
"
"
)
try
:
try
:
...
@@ -243,6 +306,11 @@ class GPM:
...
@@ -243,6 +306,11 @@ class GPM:
print
(
f
"
{
colorama
.
Fore
.
RED
}
No matching profile found for
'
{
name_or_email
}
'
{
colorama
.
Style
.
RESET_ALL
}
"
)
print
(
f
"
{
colorama
.
Fore
.
RED
}
No matching profile found for
'
{
name_or_email
}
'
{
colorama
.
Style
.
RESET_ALL
}
"
)
def
remove
(
self
,
name
):
def
remove
(
self
,
name
):
"""
Remove a profile
:param name: The name or email of the profile to remove
:return: None
"""
if
profile
:
=
self
.
match_query_to_profile
(
name
):
if
profile
:
=
self
.
match_query_to_profile
(
name
):
out_text
=
f
"
{
colorama
.
Fore
.
GREEN
}
Removed profile:
{
self
.
get_profile_format
(
profile
)
}{
colorama
.
Style
.
RESET_ALL
}
"
out_text
=
f
"
{
colorama
.
Fore
.
GREEN
}
Removed profile:
{
self
.
get_profile_format
(
profile
)
}{
colorama
.
Style
.
RESET_ALL
}
"
self
.
profiles
.
remove
(
profile
)
self
.
profiles
.
remove
(
profile
)
...
@@ -252,6 +320,10 @@ class GPM:
...
@@ -252,6 +320,10 @@ class GPM:
print
(
f
"
{
colorama
.
Fore
.
RED
}
No matching profile found for
'
{
name
}
'
{
colorama
.
Style
.
RESET_ALL
}
"
)
print
(
f
"
{
colorama
.
Fore
.
RED
}
No matching profile found for
'
{
name
}
'
{
colorama
.
Style
.
RESET_ALL
}
"
)
def
load_git_profile
(
self
)
->
Optional
[
Profile
]:
def
load_git_profile
(
self
)
->
Optional
[
Profile
]:
"""
Load the git profile from the git config
:return: The git profile
"""
name
=
os
.
popen
(
self
.
_get_git_subcommand
(
"
user.name
"
)).
read
().
strip
()
name
=
os
.
popen
(
self
.
_get_git_subcommand
(
"
user.name
"
)).
read
().
strip
()
email
=
os
.
popen
(
self
.
_get_git_subcommand
(
"
user.email
"
)).
read
().
strip
()
email
=
os
.
popen
(
self
.
_get_git_subcommand
(
"
user.email
"
)).
read
().
strip
()
try
:
try
:
...
@@ -261,6 +333,10 @@ class GPM:
...
@@ -261,6 +333,10 @@ class GPM:
return
Profile
(
name
or
""
,
email
or
""
)
return
Profile
(
name
or
""
,
email
or
""
)
def
_get_local_profile_or_create
(
self
)
->
Optional
[
Profile
]:
def
_get_local_profile_or_create
(
self
)
->
Optional
[
Profile
]:
"""
Get the local profile or create a new one
:return: The local profile
"""
profile
=
self
.
load_git_profile
()
profile
=
self
.
load_git_profile
()
if
not
profile
:
if
not
profile
:
print
(
f
"
{
colorama
.
Fore
.
YELLOW
}
No current profile set
{
colorama
.
Style
.
RESET_ALL
}
"
)
print
(
f
"
{
colorama
.
Fore
.
YELLOW
}
No current profile set
{
colorama
.
Style
.
RESET_ALL
}
"
)
...
@@ -280,6 +356,10 @@ class GPM:
...
@@ -280,6 +356,10 @@ class GPM:
return
profile
return
profile
def
current_show
(
self
):
def
current_show
(
self
):
"""
Show the current profile
:return: None
"""
profile
=
self
.
_get_local_profile_or_create
()
profile
=
self
.
_get_local_profile_or_create
()
if
not
profile
:
if
not
profile
:
return
return
...
@@ -294,12 +374,24 @@ class GPM:
...
@@ -294,12 +374,24 @@ class GPM:
sys
.
exit
(
1
)
sys
.
exit
(
1
)
def
get_profile_number
(
self
,
profile
:
Profile
)
->
int
:
def
get_profile_number
(
self
,
profile
:
Profile
)
->
int
:
"""
Returns the profile number for a given profile if it exists, otherwise -1
:param profile:
:return: The profile number for a given profile if it exists, otherwise -1
"""
for
index
,
prf
in
enumerate
(
self
.
profiles
,
start
=
1
):
for
index
,
prf
in
enumerate
(
self
.
profiles
,
start
=
1
):
if
prf
.
email
==
profile
.
email
:
if
prf
.
email
==
profile
.
email
:
return
index
return
index
return
-
1
return
-
1
def
current_edit
(
self
,
name
=
None
,
email
=
None
,
comment
=
None
):
def
current_edit
(
self
,
name
=
None
,
email
=
None
,
comment
=
None
):
"""
Edit the current profile
:param name: If provided, the new name for the profile
:param email: If provided, the new email for the profile
:param comment: If provided, the new comment for the profile
:return: None
"""
local_profile
=
self
.
_get_local_profile_or_create
()
local_profile
=
self
.
_get_local_profile_or_create
()
self
.
_validate_name_email
(
local_profile
.
email
,
local_profile
.
name
)
self
.
_validate_name_email
(
local_profile
.
email
,
local_profile
.
name
)
profile
=
self
.
_validate_query_profile_or_exit
(
local_profile
.
email
)
profile
=
self
.
_validate_query_profile_or_exit
(
local_profile
.
email
)
...
@@ -314,11 +406,21 @@ class GPM:
...
@@ -314,11 +406,21 @@ class GPM:
@staticmethod
@staticmethod
def
_validate_name_email
(
email
,
name
):
def
_validate_name_email
(
email
,
name
):
"""
Validate the name and email, exits if both are empty
:param email: The email to validate
:param name: The name to validate
:return: None
"""
if
not
name
and
not
email
:
if
not
name
and
not
email
:
print
(
f
"
{
colorama
.
Fore
.
YELLOW
}
No current profile set
{
colorama
.
Style
.
RESET_ALL
}
"
)
print
(
f
"
{
colorama
.
Fore
.
YELLOW
}
No current profile set
{
colorama
.
Style
.
RESET_ALL
}
"
)
sys
.
exit
(
1
)
sys
.
exit
(
1
)
def
current_remove
(
self
):
def
current_remove
(
self
):
"""
Remove the current profile from the list of profiles if it exists
:return: None
"""
local_profile
=
self
.
load_git_profile
()
local_profile
=
self
.
load_git_profile
()
if
not
local_profile
:
if
not
local_profile
:
print
(
f
"
{
colorama
.
Fore
.
YELLOW
}
No current profile set
{
colorama
.
Style
.
RESET_ALL
}
"
)
print
(
f
"
{
colorama
.
Fore
.
YELLOW
}
No current profile set
{
colorama
.
Style
.
RESET_ALL
}
"
)
...
@@ -331,12 +433,24 @@ class GPM:
...
@@ -331,12 +433,24 @@ class GPM:
print
(
out_text
)
print
(
out_text
)
def
_validate_query_profile_or_exit
(
self
,
email
):
def
_validate_query_profile_or_exit
(
self
,
email
):
"""
Validate the query profile or exit if it does not exist
:param email: The email to validate
:return: The profile if it exists, otherwise exits
"""
if
not
(
profile
:
=
self
.
match_query_to_profile
(
email
)):
if
not
(
profile
:
=
self
.
match_query_to_profile
(
email
)):
print
(
f
"
{
colorama
.
Fore
.
RED
}
No matching profile found for current git config
{
colorama
.
Style
.
RESET_ALL
}
"
)
print
(
f
"
{
colorama
.
Fore
.
RED
}
No matching profile found for current git config
{
colorama
.
Style
.
RESET_ALL
}
"
)
sys
.
exit
(
1
)
sys
.
exit
(
1
)
return
profile
return
profile
def
current_import
(
self
,
name
=
None
,
email
=
None
,
comment
=
None
):
def
current_import
(
self
,
name
=
None
,
email
=
None
,
comment
=
None
):
"""
Import the current profile from the git config
:param name: If provided, the new name for the profile
:param email: If provided, the new email for the profile
:param comment: If provided, the new comment for the profile
:return: None
"""
profile
=
self
.
load_git_profile
()
profile
=
self
.
load_git_profile
()
if
not
profile
:
if
not
profile
:
print
(
f
"
{
colorama
.
Fore
.
YELLOW
}
No current profile set
{
colorama
.
Style
.
RESET_ALL
}
"
)
print
(
f
"
{
colorama
.
Fore
.
YELLOW
}
No current profile set
{
colorama
.
Style
.
RESET_ALL
}
"
)
...
@@ -364,6 +478,10 @@ class GPM:
...
@@ -364,6 +478,10 @@ class GPM:
print
(
f
"
{
colorama
.
Fore
.
GREEN
}
Imported profile:
{
self
.
get_profile_format
(
profile
)
}{
colorama
.
Style
.
RESET_ALL
}
"
)
print
(
f
"
{
colorama
.
Fore
.
GREEN
}
Imported profile:
{
self
.
get_profile_format
(
profile
)
}{
colorama
.
Style
.
RESET_ALL
}
"
)
def
run
(
self
):
def
run
(
self
):
"""
Run the Git Profile Manager
:return: None
"""
parser
=
setup
()
parser
=
setup
()
args
=
parser
.
parse_args
()
args
=
parser
.
parse_args
()
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment