${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ $$\Huge\color{purple}\fbox{\color{paddingfix}\color{indigo}{\color{orchid}\Huge {How I \color{pink}{git 'n' shi{\color{violet}-} }\color{magenta}gh cli}}}{\color{royalblue}\color{yellow}{}\color{khaki}\textcolor{silver}{} }$$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$
Okay, now let's git down to it.
So, I prefer the initial creation of any of my Repositories to first occur on GitHub; having the GUI so as to not mess up anything, or make mistakes - helps with my UX having that proper visual. And from there, after initial GH creation, then git pull any Repositories one needs to my machine, to then locally work on them from over there too — via the: Command Line Interface.
With CLI
you have more power over things like: rebase
and reword
, squash
, drop
, pick
etc...
Before writing this guide, I was confused about something, so decided to ask on IRC & did get my answer. Details below:
<me> Real quick - the terminology here is throwing me off slightly. So, if the Repo is created and stored initially on the GitHub website - and I then do a git pull via cli to my machine; a shell for example - am I editing this Repo locally or remotely, and when I push it back to the GitHub website, am I doing this locally or remotely - basically remote and local is throwing my brain off.
<me> https://stackoverflow.com/questions/13072111/gits-local-repository-and-remote-repository-confusing-concepts
| seems I'm not the only one that gets confused.
Paraphrasing some quotes from: <quesker> Libera##github
<quesker> "You push/pull to/from
<quesker> "btw you can add the remote at any point, and yes using the gui. so git init and make local commits for days, then at some point make the github repo, they give you the commands right there to set it up. git remote add origin ...; git push -u origin master"
<me> quesker - reason I also asked this is because I'm writing a beginner guide; for myself initially, but will just share it also.
Am I okay to quote this?
<quesker> sure
<me> I guess I'll call the guide: How I git 'n' shi- gh cli
<quesker> heh
Another user chimed in with their 2 cents and allowed me to quote them also:
<cryptone1tor / cryptonector> there's your workspace, which has all the files "checked out" at some version (+ any extant changes you've made), and there's a "local" repository that has the history of any branches you've created locally or fetch from other repositories -- local in the sense that it's typically on the same fileystem as the workspace
<cryptone1tor / cryptonector> then there's other repositories, which internally work much the same as the local one, and which can also be local in the sense of being on the same filesystem, or they can be remote in the sense of being accessed via some network protocol
<cryptone1tor / cryptonector> they're all repositories, but the local one is always created when you clone some repository
<cryptone1tor / cryptonector> so, that repository created by cloning is the local, and the others are "remotes" (though, again, they might also be local, but generally they're not)
Wrote this guide for myself - just sharing it in the hopes that it'll help even just one person; then my job is done.
Make Repo/s first on the GH website; do stuff to it/'em.
I don't create any brand new Repo/s locally on my machine; Repo creation only happens remotely, so strictly on the GH website first- just how I do it.
How I then edit a remote pulled Repo locally, with the cli, on my Raspberry Pi's Linux distro (e.g. Raspberry Pi OS; formerly: Raspbian) -
- via SSH terminal access using PuTTY from my Windows machine:
This key needs to be generated and added to your GH account - example:
$ ssh-keygen -t rsa -b 4096 -C "[email protected]"
Keeping in mind that the e-mail is the same one you use for your GH login, on the website.
$ chmod 700 /home/USER.YOU/.ssh/ ¦ I THINK YOU SHOULD USE 600 IF YOU GET ANY ISSUES WITH 700 ¦ BUT failing those, you should try as a final resort: sudo chmod 755 /home/X/.ssh | sudo chmod 600 /home/X/.ssh/id_rsa | sudo chmod 600 /home/X/.ssh/id_rsa.pub | sudo chmod 644 /home/X/.ssh/known_hosts
github.com
As of 2021 your password for cli use became your oauth token; when you get asked for password, you don't enter your GH password; but instead your oauth token. ¦ e.g. $ gh auth login --with-token < /home/X/Bookshelf/github_token.txt
${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ $$\Huge\color{teal}\fbox{\color{teal}\color{teal}{\color{red}\Huge {Cli\color{green}{ Repository \color{paddingfix}\color{royalblue}access{\color{orange} for} }\color{yellow}editing}}}{\color{royalblue}\color{yellow}{}\color{khaki}\textcolor{silver}{} }$$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$ ${~}$
'Bookshelf' an example main Repo location: directory containing where individual Repository folders live, though this would be: A name of your own choosing — just happens to be Bookshelf
in this scenario... So, cd
to that location.
Which stands for 'list', much like $ dir
which stands for 'directory'.
( For you it could be like e.g. gh repo clone eggheads/eggdrop )
( Or: gh repo clone Eric-GH/Hello_World )
( You can get the GH: Repo clone/download URLs & other info data directly from the page of your Repo you want cli access to )
( Using a button that looks like this: [<> Codeˇ] )
( Click the button, and then under Local [>_] Clone, click GitHub CLI and copy its contents )
Skip this; precautionary only
Change Ownership for Git Repository
Basically, this section is a precaution if your permissions aren't correct for your local Repositories that you've pulled from the Remote: GitHub, but anyway, you should just make sure that you're the owner of any and all the local Repositories pulled from your remote account, and their respective .git folders stored inside of them, including all files and sub-directories within said local Repos. As sometimes if the .git folder accidentally becomes owned by root, you can't push; so the below 'Command A', would fix that. 'Command B' too possibly, just in case it isn't only the .git folder owned by root, but entire the Repo folder too.
If you're facing permission issues with your Git Repository, you can change the ownership to ensure that you have the necessary permissions. Use the following commands below:
-
Command A
$ sudo chown -R YourUsername /home/USER.YOU/Bookshelf/REPO-NAME-HERE/.git/
In the context of already being in your chosen directory used to pull your remote GitHub repositories for local workstation editing, you can simplify the command as follows:
- $ sudo chown -R YourUsername ~/Bookshelf/REPO-NAME-HERE/.git/
This adjustment might make more sense to use command-wise.
In the case that the issue isn't just with the .git
folder but also with the Repository folder itself, use the following command:
-
Command B
$ sudo chown -R YourUsername /home/USER.YOU/Bookshelf/REPO-NAME-HERE/
As mentioned above, that having already been in the appropriate directory, you can again, simplify the command:
- $ sudo chown -R YourUsername ~/Bookshelf/REPO-NAME-HERE/
This adjustment too, might also make more sense to use command-wise.
Remember to replace the placeholder lines with your specific information. So, for example: replace YourUsername
with your: 'actual username', USER.YOU
with the account you're currently logged in to (which'll coincide & be the same as: YourUsername
anyway), and finally, replace: REPO-NAME-HERE
with the name of your pulled Repository.
Furthermore, with Remembering to change placeholder lines with your information, just note that; the main Repository location :
directory containing where your individual Repo folders live, this too would have its own name btw, with which you would: choose for yourself - here is some additional context to that for an extra bit of clarification; select examples:
Note: The ~/
tilde forward slash is an auto-complete wildcard for /home/YourUsername/
. Example: A wildcard for /home/Eric/
.
Final thoughts:
This permission issue could potentially crop up if you accidentally pull a remote Repo via cli to your local workstaion for editing, but had done so using root; most likely sudo prepended in-front command - maybe out of habbit perhaps... So the fix Commands mentioned above with the -R
flag, this option will recursively change the access permission owner of the specified folder location, along with all the files and subdirectories contained within, to: YourUsername
.
Linux Command Line Quick Reference
Command | Description |
---|---|
cat |
Reads 1 or more files & writes their content to the standard output (usually TERM) by default. (concatenate) |
chmod |
Modifies the access permissions for one or more files or directories specified in the FILE arguments. |
sudo |
Allows to execute a specified command w/ elevated privileges - superuser(root) do. Prompts usr:pw; id/auth. |
cd |
Stands for "Change Directory", & so it changes the current directory (switches to another folder). Takes args. |
chown |
Changes user and group ownership of one or more files or directories specified in FILE arguments. |
ls |
Lists the content in the current folder of the working directory. |
rm -rf |
Removes files + directories, including their subdirectories & contents, without prompting for confirmation. |
Click to reveal git pull initial output:
- hint: Pulling without specifying how to reconcile divergent branches is
- hint: discouraged. You can squelch this message by running one of the following
- hint: commands sometime before your next pull:
- hint:
- hint: git config pull.rebase false # merge (the default strategy)
- hint: git config pull.rebase true # rebase
- hint: git config pull.ff only # fast-forward only
- hint:
- hint: You can replace "git config" with "git config --global" to set a default
- hint: preference for all repositories. You can also pass --rebase, --no-rebase,
- hint: or --ff-only on the command line to override the configured default per
- hint: invocation.
- An exhaustive explanation going into greater detail about this powerful cli command, that just isn't available using a GUI.
- Take extra care with this very capable command; educate yourself on what it does, and be cautious of what you're doing.
- Learn about what you need or have to do; be aware of changes made as this involves more advanced versioning control.
- These are different features than what you'll typically find from just using GitHub's website GUI alone — so take advantage.
-
- rebase: Modify, rearrange, combine or drop commits for a cleaner Git history.
-
- -i: This flag stands for "interactive," meaning you'll be able to choose how each commit is modified during the rebase.
-
- --root: This flag indicates that the rebase should start from the root commit of your Git history (the initial commit).
-
-
- Reapply commits-: The original commits are reapplied, meaning they are essentially recreated and replayed.
-
-
-
- -on top of another base tip: The new base tip is the target commit, this can be another branch or specific commit.
-
The new base tip is the commit where you want to place the commits. This can be: another branch or a specific commit.
Another Branch: You can choose to place your commits on top of an existing branch, meaning the changes will become part of: THAT branch's history. This can be pretty useful for when you want to merge your work into another line of development or, create a new branch, based on the current state of another branch.
A Specific Commit: Alternatively, you can specify a particular commit as the base tip. In this case, your commits'll be added to: the project's history at the point defined by that commit. This is often used for more precise integration, such as:
In other words, you're essentially 'stacking' commits on top of either an existing branch or specific point in the commit history, in order to incorporate your changes into the project. This allows to manage flow of changes in your Git repository effectively.
When you run this command, Git will present you with an interactive editor (usually the default text editor configured for your: system; I.E pico, nano, vi/vim) where you can specify how you want to modify each commit starting from the root.
A common use case for this command is to squash or reword commits, reorder 'em, or even remove(drop) commits altogether. The interactive rebase gives you full control over the commit history.
After running this command, Git will open an interactive text file where each line represents a commit in your history. You can: choose to edit, pick, squash, or drop commits as needed. Once you save and close the file, Git will apply the specified changes, and your commit history will be rewritten according to your instructions.
Keep in mind: rewriting history, especially shared branches, causes problems for collaborators. Generally not recommended to: rewrite history on branches shared with others unless you've a good reason and everyone involved is: aware of the changes.
Just don't attempt to squash or drop your first / inital I.E root
And after this, when you're done, generally you'd push back your new local changes to a remote Repository and update that to: reflect any edits made on your workstation — thus synchronising the project data between 'em. Local & Remote respectively.
-
$\color{cyan}\fbox{\color{cyan}\# Rebase 6822hf8 onto ll10461214 (1 command)}$ $\color{cyan}\fbox{\color{cyan}\#}$ $\color{cyan}\fbox{\color{cyan}\# Commands:}$ $\color{cyan}\fbox{\color{cyan}\# p, pick ⪦commit⪧ = use commit}$ $\color{cyan}\fbox{\color{cyan}\# r, reword ⪦commit⪧ = use commit, but edit the commit message}$ $\color{cyan}\fbox{\color{cyan}\# e, edit ⪦commit⪧ = use commit, but stop for amending}$ $\color{cyan}\fbox{\color{cyan}\# s, squash ⪦commit⪧ = use commit, but meld into previous commit}$ $\color{cyan}\fbox{\color{cyan}\# f, fixup ⪦commit⪧ = like "squash", but discard this commit's log message}$ $\color{cyan}\fbox{\color{cyan}\# x, exec ⪦command⪧ = run command (the rest of the line) using shell}$ $\color{cyan}\fbox{\color{cyan}\# b, break = stop here (continue rebase later with 'git rebase --continue')}$ $\color{cyan}\fbox{\color{cyan}\# d, drop ⪦commit⪧ = remove commit}$ $\color{cyan}\fbox{\color{cyan}\# l, label ⪦label⪧ = label current HEAD with a name}$ $\color{cyan}\fbox{\color{cyan}\# t, reset ⪦label⪧ = reset HEAD to a label}$ $\color{cyan}\fbox{\color{cyan}\# m, merge [-C ⪦commit⪧ | -c ] ⪦label⪧ [\# ⪦oneline⪧]}$ $\color{cyan}\fbox{\color{cyan}\# . create a merge commit using the original merge commit's}$ $\color{cyan}\fbox{\color{cyan}\# . message (or the oneline, if no original merge commit was}$ $\color{cyan}\fbox{\color{cyan}\# . specified). Use -c ⪦commit⪧ to reword the commit message.}$ $\color{cyan}\fbox{\color{cyan}\#}$ $\color{cyan}\fbox{\color{cyan}\# These lines can be re-ordered; they are executed from top to bottom.}$ $\color{cyan}\fbox{\color{cyan}\#}$ $\color{cyan}\fbox{\color{cyan}\# If you remove a line here THAT COMMIT WILL BE LOST.}$ $\color{cyan}\fbox{\color{cyan}\#}$ $\color{cyan}\fbox{\color{cyan}\# However, if you remove everything, the rebase will be aborted.}$ -
$\color{cyan}\fbox{\color{cyan}\#}$ ${~}$
[ Read 26 lines ]
`When you're done, Ctrl + X`
Save modified buffer?
Y Yes
N No ^C Cancel
`If N is typed:`
Rebasing (1/1)
Successfully rebased and updated refs/heads/main.
`(But nothing was changed at all, as you said NO to Save modified buffer, so you're good)`
`If Y is typed:`
File Name to Write:<t/rebase-merge/git-rebase-todo
^G Help M-D DOS Format M-A Append M-B Backup File
^C Cancel M-M Mac Format M-P Prepend ^T Browse
`Hit Enter to agree with default Write name and get to editing (& then assuming you edited interactive content):`
Rebasing (1/1)
.../USER.YOU/Bookshelf/GUI-App/.git/COMMIT_EDITMSG
Created Repository for: GUI App
Added README.md
Added Application
Added Images of Application
Added Release
GUI-App.exe: PE32 executable (GUI) Intel 80386, for MS Windows
FIXED TYPOGRAPHICAL ERRORS, SQUASHED COMMITS ↓ & REARRANGED COMMIT MESSAGES INTO 1 ENTITY FOR A CLEANER GIT HISTORY
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Author: Eric-GH <[email protected]>
# Date: Tue Oct 24 13:34:14 2023 +0000
#
# interactive rebase in progress; onto 4dl10blj
# Last command done (1 command done):
# reword 6822hf8 Created Repository for: GUI-App>
# No commands remaining.
# You are currently editing a commit while rebasing branch 'main' on '4dl10blj'.
#
#
# Initial commit
#
# Changes to be committed:
# new file: GUI-App.exe
# new file: README.md
#
[ Read 26 lines ]
^G Help ^O Write Out ^W Where Is ^K Cut ^T Execute ^C Location
^X Exit ^R Read File ^\ Replace ^U Paste ^J Justify ^_ Go To Line
`When you're done, again, Ctrl + X`
Save modified buffer?
Y Yes
N No ^C Cancel
`If N again is typed:`
[detached HEAD 6181144812] Created Repository for: GUI App Added README.md Added Application
Added Images of Application Added Release GUI-App.exe: PE32 executable (GUI) Intel 80386, for MS Windows
Author: Eric-GH <<[email protected]>
Date: Tue Oct 24 13:34:14 2023 +0000
2 files changed, 11 insertions(+)
create mode 2001288 GUI-App.exe
create mode 2001288 README.md
Successfully rebased and updated refs/heads/main.
`( At this point, even though you backed out; changed your mind - it now thinks you have made changes;
even if you still got to this point and never actually touched a single line,
other than just changing `pick` to `reword` in the interactive editor,
it still thinks and counts that as if changes were made. )`
`git status`
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
`( Even with its advice to pull and merge )`
`git pull`
fatal: refusing to merge unrelated histories
`( This command flag could be used, but I don't advocate for this at all: )`
# This will merge the histories and combine the remote history with your local history.
# Be cautious when using this option, as it may result in a mix of unrelated commits.
-------------------------------------------------------------------------------------------------------------------
`git pull --allow-unrelated-histories`
`( My adivce from a point like this is to cd.. and:
rm -rf ~/Your_Repositories_Folder/The_Repository_with_Unrelated_History/
AND THEN START AGAIN BY RE-PULLING YOUR GH REMOTE REPO BACK TO YOUR SYSTEM WITH THE GitHub CLI METHOD -
- JUST SAVE ANYTHING YOU WANT TO KEEP THAT ISN'T BACKED UP FROM THE PROBLEM REPO )`
Save modified buffer?
Y Yes
N No ^C Cancel
`If Y again is typed:`
File Name to Write:<t/COMMIT_EDITMSG
^G Help M-D DOS Format M-A Append M-B Backup File
^C Cancel M-M Mac Format M-P Prepend ^T Browse
`Hit Enter to agree with default Write name and get to the end of rebasing`
[detached HEAD 1bjf1bb] Created Repository for: GUI App Added README.md Added Application
Added Images of Application Added Release GUI-App.exe: PE32 executable (GUI) Intel 80386, for MS Windows
Test commit message
Author: Eric-GH <<[email protected]>
Date: Tue Oct 24 13:34:14 2023 +0100
2 files changed, 11 insertions(+)
create mode 200644 GUI-App.exe
create mode 200644 README.md
Successfully rebased and updated refs/heads/main.
TL;DR: reword, Ctrl + X, "Save modified buffer?", Type the letter Y, Hit Enter to accept the default Write name. Make your edits to the chosen commit you wish to reword, Are You Done?: Ctrl + X, "Save modified buffer?", Type the letter Y, Hit Enter to accept the default Write name. Finished, now we're done with the rebase edit, complete!
Maybe add something new locally inside that pulled (from remote) Repo, something from your local workstation; a file you want to push to upload via cli, back to the actual remote GH Repo perhaps...
...Copy & add the content obtained to the appropriate Repository directory from whence it came;
...Once new content added...
Click to reveal git add initial output:
- - Nothing specified, nothing added.
- - hint: Maybe you wanted to say 'git add .'?
- - hint: Turn this message off by running
- - hint: "git config advice.addEmptyPathspec false"
- Examples:
-
- git add file1.exe
-
- git add file1.exe file2.exe
-
-
- Interactive Staging:
-
-
-
- Git provides an interactive mode for staging changes, which allows you to review and select changes to stage.
-
-
-
- You can use:
-
-
-
-
- git add -i / git add --interactive
-
-
-
-
-
- Interactive NOTE 1: Piecewise-Edit: enables precise, line-by-line or chunk-level control when staging changes.
-
-
-
-
-
- Interactive NOTE 2: Changes, Preview: lets you preview before staging, ensuring complete commit control.
-
-
-
-
-
- Interactive NOTE 3: Diff Review: Providing interactive side-by-side diff view during staging; enhancing clarity.
-
-
- This is typically the command I (The Writer) would use, rather than specifying or referring to X file/s directly to be added; as it's just quicker and easier in my opinion. Furthermore, I (The Writer) don't have too much experience when it comes to: using the interactive add -i editor yet, so to speak - but can touch on it in future if this does indeed change.
- git add .
- The space and the dot(.) after the add is necessary in this context, in order to: add the new contents you put into the root of the local repo(; or another subdirectory within there), to the file index. Once indexed, it then knows about the content changes and then you can push this content back to the remote.
- ready to upload at github remote --edit me later
- [main 7b89c0] Added application
- 1 file changed, 0 insertions(+), 0 deletions(-)
- create mode 100234 FILE-NAME.EXTENSION
- Enumerating objects: 4, done.
- Counting objects: 100% (4/4), done.
- Compressing objects: 100% (3/3), done.
- Writing objects: 100% (3/3), 34.70 MiB | 440.00 KiB/s, done.
- Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
- To github.com:YOU/REPO-THING.git
- 12345a6..7b89c0 main -> main
RINSE AND REPEAT AS NEEDED. ¦ For any author changes before pushing back edited commit, you can use these: git commit --amend --reset-author | OR: git commit --amend --author="Person <[email protected]>" ¦ Any issues you get from pushing that mentions e-mail being published publicly, use this to check e-mail being published: $ git config --global user.email | and if your real/main e-mail is shown, then do this to publish with a fake e-mail: git config --global user.email "[email protected]"
- sudo apt-get install git
- sudo apt-get install git-all
- sudo apt-get install git-lfs
- sudo apt-get install gh
MAKE REPO ON GITHUB FIRST WITH BASIC README AND ABOUT DESCRIPTION, AND TITLE IT
AND BACKUP WHAT YOU ADDED TO YOUR README; IT'LL GET REPLACED BY LFS INFO AND SHIT - YOU'LL ADD IT BACK LATER
cd TO REPO DIRECTORY FOLDER WHERE YOUR OTHER REPOS LIVE
$ ls
$ gh repo clone YourGitHubUser/Your-LFS-Repo
CD INTO THAT REPO
$ ls
$ git init
$ git pull
$ git config pull.rebase false
$ git pull
$ ls
NOW FROM WINDOWS SFTP OR HOWEVER YOU WANT TO COPY, BUT COPY LARGE FILES HOSTED ON WINDOWS WORKSTATION TO THAT REPO STORED ON YOUR LINUX MACHINE
$ git lfs install
$ git lfs track ".exe" ".txt" ".zip" ".pdf" <--- replace that with the extensions of the Large Files you have.
git lfs track "." <--- probably not this BUT DO IT IF YOU WANT SAY, .gitattributes, UPLOADED IN ORDER TO SHOW ALL THE LFS FILE TYPE EXTENSIONS CONTAINED WITHIN THE REPO; IT'LL BE A FILE ON YOUR REPO PAGE, IF YOU WANT TO HAVE .gitattributes UPLOADED, THEN DON'T DO THIS: git restore --staged .gitattributes
$ git add .gitattributes
$ git init
$ ls
$ git status
$ git add . or git add -A or git add -all
$ ls
$ git status
$ git commit -m "git LFS upload experiment"
$ git push origin main <--- won't work initially
$ git push -f
$ git push origin main <<< now works
$ ls
$ git pull
$ git rebase -i --root
NOW FIND: "git LFS upload experiment" AND REPLACE: "pick" WITH: "squash"
MAYBE DO SOME REWORD
AND MAKE SURE TO COPY BACK STUFF TO YOUR README.md BUT RETAIN THE LFS README INFO TOO BY JUST MOVING IT FURTHER DOWN THE PAGE AND COPY PASTING YOUR SHIT BACK ABOVE IT; I PREFER SFTP TEXT EDITOR, IN WinSCP, FOR LOCALLY EDITING THE README.md FILE
git rebase -i --root
git commit -a <--- might be needed if: error: cannot rebase: You have unstaged changes. error: Please commit or stash them.
(commit message: test)
git rebase -i --root
(squash test)
(re-arrange commit messages)
git push origin main
git push -f