Git
2024-09-26
1. init
This will create a local git repository.
git init
Also you can git it an initial branch name:
git init --initial-branch=trunk
2. config
You can configure git to behave how you like it to do.
user.name = Your name # critical
user.email = your@email.com # critical
core.editor = vim # default git's text editor
core.autocrlf = input # for fixing EOL character across multiple OSs
commit.gpgsign = true # signed commits using GPG
tag.gpgSign = true # // tags // //
merge.ff = true # fast-forward only when merging/pulling/etc..
init.defaultBranch = master # default branch for `git init`
color.pager = true # colored output e.g `git log --oneline`
git config --global user.name "Your Name"
git config --global user.email you@email.com
git config --global merge.ff true
3. staging area
Middle stage between Untracked files and Tracked files.
git add .
git add <path/to/file>
|
To see or do anything on files in the staging area (beside committing them),
you need to pass
|
4. status
Show the status of files in current repository.
git status
On branch notes/git Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: git.adoc Untracked files: (use "git add <file>..." to include in what will be committed) .session.vim/ asciidoctor-theme.yml git.pdf no changes added to commit (use "git add" and/or "git commit -a")
git status -s
M git.adoc ?? .session.vim/ ?? asciidoctor-theme.yml ?? git.pdf
|
|
git status -s .
M git.adoc ?? .session.vim/ ?? asciidoctor-theme.yml ?? git.pdf
5. commit
Look at commit as adding changes to the git history.
git commit
The command above will open the git’s text editor for you to write a commit message for the changes you’ve made. To avoid that you can give the message at the command line:
git commit -m 'my very obvious commit message'
--no-gpg-sign # don't sign the commit, just for this one
--amend # apply changes to the last commit, no new commit
# Warning: be careful with this, it can mess things up
--no-edit # used with `--amend`, don't change commit message
5.1. authenticate
For committing you need to be authenticated. You can this in a global scope (which is recommended for most users) or do it in a per-repo basis:
git config --global user.name 'You Name'
git config --global user.email 'you@email.com'
git config user.name 'You Name'
git config user.email 'you@email.com'
|
Keep that in mind for working with services like github.com and gitlab.com, the email you provide to the git, must be the same email as the one which you gave these services. |
6. log
Shows information about the current repository such as branches, commits, HEAD position, etc.
git log
commit f80c36530015cb932a4d60c06f94f93cf22570cf Author: Hossein Esmail <hosteam01@gmail.com> Date: Sun Oct 13 23:38:50 2024 +0330 more / better notes for docker commit 6e057f8ee426d75045693f417d9769c8ff2093bd Author: Hossein Esmail <hosteam01@gmail.com> Date: Fri Oct 11 15:17:28 2024 +0330 import contents commit a9555db8084619a61b2da3c51ae04c972a242f94 Author: Hos Es <62862610+hossein-lap@users.noreply.github.com> Date: Wed Oct 9 20:13:41 2024 +0330 Initial commit
--oneline # show short commit hash and only the commit message
--all # show all branches
--graph # draw the graph for branches
--stat # show changed files
--show-signature # show gpg signature
git log --all --oneline --graph
* afd0009 (notes/perl, origin/notes/perl) Add array section * f80c365 (HEAD -> notes/git, origin/notes/git) more docker notes * 6e057f8 import contents * a9555db Initial commit
HEAD is a name which points to your current working area's position in the
git repository. In the above output, inside the parentheses on the second line
you can see HEAD -> notes/git
, that mean HEAD is pointing at notes/git
branch. notes/git
is the name of the branch that I’m writing this document
inside of it that will be merged later with master
branch of this repository.
Now that we know HEAD is the current position, let’s be a little more proactive
shall we? Okay, what about pointing at one previous position or
two previous? Easy, HEAD~1
and HEAD~2
are for that.
|
|
7. remotes
The remote URL(s) that you are/will be working with. Most of the time it’s called origin but remember, it’s just a name which points to an URL.
git remote
origin
git remote -v
origin git@github.com:hossein-lap/blog.posts.git (fetch) origin git@github.com:hossein-lap/blog.posts.git (push)
git remote add <name> <url> # add new remote URL
git remote remove <name> # remove existing remote URL
-
remote URL can be another directory/folder on the same machine. git does not care.
-
If you want to connect a local repository to a remote one, you need to create the remote yourself
|
Let’s say you are working on a project on your local machine and now you’ve
decided to share it on github, you go create the repository on github, and
create a remote using |
7.1. ssh vs https
Most developers prefer using ssh
when it comes to working with remote
repositories. ssh
has a SHA-256 hash-based key authentication method.
Unlike https
which requires username and password each time for the
authentication.
ssh-keygen
|
After creating a ssh key-pair, you need to add the public pair of the key to
your github/gitlab account. Look for a |
origin git@github.com:hossein-lap/blog.posts.git (fetch) origin git@github.com:hossein-lap/blog.posts.git (push)
origin https://github.com/hossein-lap/blog.posts (fetch) origin https://github.com/hossein-lap/blog.posts (push)
8. clone
Cloning a repository is like downloading it from your local machine but with or without the commit histories.
git clone <url>
git clone https://github.com/hossein-lap/blog.posts
git clone git@github.com:hossein-lap/blog.posts.git
--depth <number> # depths of previous commit history
--branch <name> # move HEAD to the <name> branch after clone
--origin <name> # use <name> instead of default `origin` for remote
--bare # clone the bare repo (useful but very advanced)
# see the last section for more information.
9. fetch
Synchronize the local repository with the remote repository. This does not change anything in the current working area. Just synchronizing.
git fetch --all # fetch all changes from all branches
git fetch --unshallow # fetch all the missing contents from remote
10. push
Upload the git history from local to the remote.
git push <remote-name> <branch-name>
git push origin master
|
Forced push
You need to use
Use this with caution
If the branch if protected you cannot use |
11. pull
Get the changes from remote repository to the local repository.
git pull origin master
|
Be careful what branch are you currently on and what branch are you pulling from. This can mess things up very easily. |
12. branch
Working with branches. Branches are created from one point in the history which is you current branch
git branch
* notes/git
git switch -C <new-branch-name>
Above command will create a new branch from your current position on the git history (your current branch, your current commit) and switch to it.
Alternatively you can use checkout
(sometimes you need to use checkout
) but
the checkout
command does a lot more than creating/switching branches.
It can be dangerous.
git checkout -b <new-branch-name>
git checkout -
git switch -
13. stash
To be able to change branches your current working area must be clean. Now imagine
you’re middle of working on something and something new comes up which is
important, git won’t allow you to change your branch until you commit your
changes (make your working area clean) so what you’re gonna do? Apply a temp
commit? (you can do that on paper but it’s advised against doing this). Here
stash
comes to save the day.
git stash
takes all your changes (on tracked files only) and temporarily
moves them on stash area so you can access it (move it back to your working
area) after you’ve did you explorations.
Let’s see how git log
looks like before stashing the changes:
git log --oneline --all --graph
* afd0009 Add array section * f80c365 more / better notes for docker * 6e057f8 import contents * a9555db Initial commit
git stash push
Saved working directory and index state WIP on notes/git: f80c365 more / better notes for docker
Let’s see how git log
looks like at this point
git log --oneline --all --graph
* aa604f7 WIP on notes/git: f80c365 more / better notes for docker |\ | * 977460d index on notes/git: f80c365 more / better notes for docker |/ | * afd0009 Add array section |/ * f80c365 more / better notes for docker * 6e057f8 import contents * a9555db Initial commit
See all the new forks and diversions from the commits?
* aa604f7 WIP on notes/git: f80c365 more / better notes for docker |\ | * 977460d index on notes/git: f80c365 more / better notes for docker |/ | * afd0009 Add array section |/ * f80c365 more / better notes for docker
git stash pop
On branch notes/git Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: git.adoc Untracked files: (use "git add <file>..." to include in what will be committed) .session.vim/ asciidoctor-theme.yml git.pdf git.xml no changes added to commit (use "git add" and/or "git commit -a") Dropped refs/stash@{0} (a5eec65b3011ad09cd73c9845646ea1b87659f9e)
Now everything is exactly how it was before stashing:
git log --oneline --all --graph
* afd0009 Add array section * f80c365 more / better notes for docker * 6e057f8 import contents * a9555db Initial commit
|
stash has a stack-like structure. |
|
Alternatively you can use a |
14. rebase
Rebasing a branch is pulling the point branch was forked (created from) to a new point.
Assume the following history exists and the current branch is "topic": A---B---C topic / D---E---F---G master From this point, the result of either of the following commands: git rebase master git rebase master topic would be: A'--B'--C' topic / D---E---F---G master
|
All the git manpages are available through either git rebase --help man git-rebase |
15. restore
Restores the state of file(s) to a previous or current state in git history.
|
|
The above command will remove all changes on the current working directory which are not in neither staging area nor have been committed.
The .
means current working directory. You can replace it with file name(s)
or (some) directory.
16. reset
Okay, now we are entering the DANGER zone. The reset command will remove
your commit history. Especially with the --hard
flag.
|
|
17. patching
You can get a diff file using git diff
command and use it later.
These "diff" files contains all changes on file(s) which can be applied on same
files somewhere else.
git diff HEAD~1 HEAD > <file-name>
git diff HEAD~1 HEAD > test-changes.diff
patch -p1 < <file-name>
patch -p1 < test-changes.diff
|
Keep that in mind which the sequence of stages/commits must be older to newer if you want to apply the changes and newer to older if you want to revert the changes. Reverts
|
|
This process is called patching or applying patch. |
|
When you are applying patch(es), you must be at the exact directory that you’ve get the diff file from. On the other words, applying patches only works when you are applying them at the same root directory of getting the patch (diff file). |
git diff Makefile default-theme.yml
diff --git a/Makefile b/Makefile
index 16ab52b..db20550 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
NAME = ttr
all:
- asciidoctor -a stylesheet=my-stylesheet.css ttr.adoc
- # asciidoctor -a linkcss -a copycss README.adoc
+ # asciidoctor -a stylesheet=my-stylesheet.css ttr.adoc
+ asciidoctor -a linkcss -a copycss ttr.adoc
asciidoctor-pdf ttr.adoc --theme hos.yml
diff --git a/default-theme.yml b/default-theme.yml
index f23711b..889785a 100644
--- a/default-theme.yml
+++ b/default-theme.yml
@@ -43,11 +43,11 @@ base:
border_color: EEEEEE
border_radius: 4
border_width: 0.5
-base:
- font:
- color: #333333
- family: Times-Roman
- size: 12
+# base:
+# font:
+# color: #333333
+# family: Times-Roman
+# size: 12
role:
lead:
font_size: $base_font_size_large
18. git worktree and bare repos
This section is a very advanced topic and is unique approach to solving some problems like changing branches and checking them out at the same time.
In this approach, every branch is a separated directory. To changes branches
you need to change your current working directory (e.g
cd ../<branch-name>
).
git clone --bare git@github.com:hossein-lap/blog.git blog.git
git worktree add <branch-name>
git worktree add -b <new-branch-name> <new-directory-name>
git worktree remove <branch-name>
|
Cloning a repository bare, requires to add the |
|
You still can change (mess things up if you will) other branches while you are on a different branch. Be careful with that. |
18.1. Wrapper script
#!/usr/bin/env bash
set -e
# help function
prompt=$(echo ${0} | awk -F '/' '{print $NF;}')
help() {
cat << EOF
${prompt}: setup git worktree and bare repo
usage: [-h] [-u url] [-d directory] [-a extra_args]
• arguemts:
-u --url repo url (ssh)
-d --dir directory name
-a --args extra args (to pass to the git)
-h --help print this message
• example:
${prompt} -u gitlab.com:hos-workflow/scripts -d test.git -a '--depth 1'
• running without any arguments will show this message
EOF
}
# argument parsing
while [ "${#}" -gt 0 ]; do
case ${1} in
-u|--url)
input="${2}"
shift
;;
-d|--directory)
output="${2}"
shift
;;
-h|--help)
help
exit 0
;;
-a|--args)
args="${args} ${2}"
shift
;;
*)
echo "Unknown parameter passed: ${1}"
exit 1
;;
esac
shift
done
# checking args
if [ -z "${input}" ]; then
printf '%s\n\n' "No url is specified" 1>&2
help
exit 1
fi
if [ -z "${output}" ]; then
printf \
"No directory name is specified, " \
"Using default directory name..\n" \
1>&2
output="$(echo ${input} | awk -F '/' '{print $NF;}')"
fi
# start
git clone ${args} --bare git@${input} ${output}
cd ${output}
mkdir .bare
mv * .bare
echo "gitdir: ./.bare" > .git
check_branch=$(git --no-pager branch | grep -v '*\|+' | awk '{print $1;}' | wc -l)
if [ "${check_branch}" -gt 0 ]; then
for i in $(git --no-pager branch | sed 's/^[*+]/ /' | awk '{print $1;}'); do
git worktree add "${i}" "${i}"
done
else
i=$(git --no-pager branch | awk '{print $NF;}')
git worktree add "${i}" "${i}"
fi
# git config remote.origin.url "git@${input}"
git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
git fetch