Services
    
    Blog
    
    Français
    
  
This tutorial aims to teach Git basics to work in a team.
In this tutorial, you are going to have 3 roles:
First, create what will serve as central git server for this tutorial:
git init --bare git-central
This will output something about master branch being non-inclusive, you can
discard this message.
But it will specifically output:
Initialized empty Git repository in ~/git-central/
Alice and Bob will pull and push sources to ~/git-central/ just like if it
was a gitlab server, because it’s a “bare” repository.
Alice will create the initial commit, run this command to clone the repository for Alice:
git clone git-central git-alice
Will output:
Cloning into 'git-alice'...
warning: You appear to have cloned an empty repository.
done.
Git is a decentralized Version Control System, as such, it supports as many remotes as necessary.
When git clones a repository, it automatically configures a remote with the
name origin and with the URL that it has cloned from.
You can see it with:
cat git-alice/.git/config
Output:
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
        url = /home/jpic/git-central
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
        remote = origin
        merge = refs/heads/master
Or, in the git-alice directory, run the following command:
git remote -v
Will output:
origin  /home/jpic/git-central (fetch)
origin  /home/jpic/git-central (push)
Now, let’s create a bash script in there:
cd git-alice
echo 'echo hello' > script.sh
git statusLet’s see what git thinks of our script with the following command:
git status
Will output:
On branch master
No commits yet
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        script.sh
nothing added to commit but untracked files present (use "git add" to track)
This means that git sees a script.sh file in the repository, but that it
doesn’t track it.
First, let’s “stage” our new file for commit:
git add script.sh
Run again:
git status
See how the output has changed:
On branch master
No commits yet
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   script.sh
This shows that git has staged a new file for the next commit.
git diff --cachedTo see what changes git will include in the commit prior to running git-commit, run:
git diff --cached
This will show the “diff”, in unified patch format, that is going to be commited:
diff --git a/script.sh b/script.sh
new file mode 100644
index 0000000..2f08be9
--- /dev/null
+++ b/script.sh
@@ -0,0 +1 @@
+echo hello
Let’s dissect these lines:
diff --git a/script.sh b/script.sh means it’s showing the diff between version a and
verion b of script.sh.new file mode 100644 means that it’s a new file with mode 644: git
tracks file modes,index 0000000..2f08be9 means that this diff goes from hash 0 to hash
.2f08be9: a git hash is a hash of the contents of a commit (the diff) +
the hash of the parent commit--- /dev/null: means that previously, there was no file in script.sh+++ b/script.sh: means that all lines added by version b of script.sh
will be represented by a leading + sign@@ -0,0 +1 @@: means that we’re starting at the first line+echo hello: means that we’re adding the line echo hellogit commitNow that we have proof-read our patch thanks to the git diff --cached
command, we are ready to commit:
git commit -m "Added hello world script"
This will output:
[master (root-commit) ec277005] Added hello world script
 1 file changed, 1 insertion(+)
 create mode 100644 script.sh
[master (root-commit) ec277005] means that the commit is on branch master,
it’s commit short hash is ec277005, it’s a root-commit meaning that it
doesn’t have a parent commit.1 file changed, 1 insertion(+) this is the “stat” of a commit,create mode 100644 script.sh is the list of files added, changed, or
deleted by this commit.git showLet’s see the commit with the following command:
git show
Output:
commit ec27700523fc4cdae37de6156ecf1784da24460b (HEAD -> master)
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:02:58 2023 +0100
    Added hello world script
diff --git a/script.sh b/script.sh
new file mode 100644
index 0000000..2f08be9
--- /dev/null
+++ b/script.sh
@@ -0,0 +1 @@
+echo hello
This shows the whole commit:
You could also pass a commit hash as argument to git show like git show deadbeef to inspect a specific commit.
git push <remote> <ref>Since Alice knows she’s writing the first commit, we can go ahead and push for her.
We will push the master branch of the local repository, which we can refer to
as master, to the git-central which is our origin remote which we will
refer to as origin:
git push origin master
Will output:
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 245 bytes | 122.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To /home/jpic/git-central
 * [new branch]      master -> master
As you can see, it has created a new branch on the remote with the name
master and has pushed the commits of our local master branch there.
Now, let’s clone a git repository for Bob so that he can also contribute:
git clone ~/git-central ~/git-bob
This will output the following:
Cloning into '/home/jpic/git-bob'...
done.
git-logLet’s make sure Bob’s “working copy” is as expected:
cd ~/git-bob
git status
git log --stat
This will show the history of commits on the current branch.
A great command to browse commits is tig but it’s not included in the git rpm
repository.
git add -pLet’s do a change as Bob:
cd ~/git-bob
echo 'echo hello world' > script.sh
And run the interactive git-add command. Unless you are adding a new
file, you must always use git add -p because this forces you to read what
is going to be staged for commit, but also lets you interactively modify the
patch on the fly.
git add -p
It will output the following and wait for user input:
diff --git a/script.sh b/script.sh
index 2f08be9..7c0dffd 100644
--- a/script.sh
+++ b/script.sh
@@ -1 +1 @@
-echo hello
+echo hello world
(1/1) Stage this hunk [y,n,q,a,d,e,?]?
This means that git has found a diff in script.sh, between version “a” which
is represented by - characters, and version “b” which is represented by +
signs.
Let’s type ? to see the options we have here:
(1/1) Stage this hunk [y,n,q,a,d,e,?]? ?
y - stage this hunk
n - do not stage this hunk
q - quit; do not stage this hunk or any of the remaining ones
a - stage this hunk and all later hunks in the file
d - do not stage this hunk or any of the later hunks in the file
e - manually edit the current hunk
? - print help
As we can see, we need to type y to stage this part (“hunk”) of the patch:
@@ -1 +1 @@
-echo hello
+echo hello world
(1/1) Stage this hunk [y,n,q,a,d,e,?]? y
Let’s review our changes staged for commit prior to commiting:
git diff --cached
Will output the following:
diff --git a/script.sh b/script.sh
index 2f08be9..7c0dffd 100644
--- a/script.sh
+++ b/script.sh
@@ -1 +1 @@
-echo hello
+echo hello world
This is what we want to commit, let’s go:
git commit -m "Completed hello world script"
And let’s push:
git push origin master
So far so good! Let’s look for troubble now…
At the same time, Alice decided to translate the hello message:
cd ~/git-alice
echo echo bonjour > script.sh
git add -p script.sh
git commit -m "Translate into french"
So far so good: Alice only produced changes in her local “working copy”. But troubble arise in paradise when she try to push:
git push origin master
Will output:
To /home/jpic/git-central
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to '/home/jpic/git-central'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
As you can see, the git central server rejected our push of master.
The explanation is pretty explicit, but in my experience people tend to not read the git output at all and expect it to always work with whatever they do.
It’s complaining that there are changes on the master branch of the origin
server that are not here in Alice’s working copy.
Let’s check the commit log in Alice’s working copy:
cd ~/git-alice
git log
It is:
commit 77181ccf6a2cc6663a854d718099201fd5f78f40 (HEAD -> master)
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:27:33 2023 +0100
    Translate into french
commit ec27700523fc4cdae37de6156ecf1784da24460b (origin/master)
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:02:58 2023 +0100
    Added hello world script
This means that git in git-alice beleives that:
master branch on origin remote is at commit ec277005,77181cc,master branch in the local working copy is pointing at 8bd47fa,Now let’s check in Bob’s:
cd ~/git-bob
git log
commit 59193aed08fd896b2c39b6778ef928faffc888a6 (HEAD -> master, origin/master, origin/HEAD)
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:24:58 2023 +0100
    Completed hello world script
commit ec27700523fc4cdae37de6156ecf1784da24460b
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:02:58 2023 +0100
    Added hello world script
As you can see, git in git-bob believes that:
master branch on origin remote is at commit 59193ae,master branch on the local working copy is at commit 59193ae,ec277005,So basically, Alice is left with the following problems:
ec277005 that the origin remote
already has a child commit for, namely 59193ae, and git can’t have
commits with several parents (in general, exceptions to that rule will not be
covered in this tutorial).git fetchFirst thing you should do prior to working in any git repository: make sure you have an up to date cache of the server.
Run the following:
cd ~/git-alice
git fetch
Outputs:
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 267 bytes | 89.00 KiB/s, done.
From /home/jpic/git-central
   ec27700..59193ae  master     -> origin/master
This means that git fetch has fetched new commits in origin/master, namely
all commits between ec277005 which we already had, and 59193ae which is the
head of the master branch of the origin server, which we’ll reference as
origin/master from now on.
Alice can compare the difference between the commit tree of her master
branch, and the origin/master branch with the git log command:
git log
It will output the same as before, except that it doesn’t believe that
origin/master is pointing to ec277005 anymore, so that (origin/master) we
had next to that commit is gone:
commit 77181ccf6a2cc6663a854d718099201fd5f78f40 (HEAD -> master)
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:38:08 2023 +0100
    Translate into french
commit ec27700523fc4cdae37de6156ecf1784da24460b
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:36:26 2023 +0100
    Added hello world script
Now that we have fetched the changes from the origin server into our local
cache, we can also check its logs:
git log origin/master
Will output:
commit 59193aed08fd896b2c39b6778ef928faffc888a6 (origin/master)
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:37:45 2023 +0100
    Completed hello world script
commit ec27700523fc4cdae37de6156ecf1784da24460b
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:36:26 2023 +0100
    Added hello world script
As you can see, Alice’s git now knows that origin/master points at 59193ae
which is the commit that Bob added.
It also knows that this commit is the child of ec277005, and this is why it
doesn’t want to push our 77181cc commit on top of it.
git-rebaseAlice is stuck at this point and her only way out is to rewrite history, which
is what the git rebase command is for. With git-rebase you can tell git to
rewrite all your new commits on top of a given “head”.
Rebasing your master on top of origin/master means git will:
master and from origin/master,master and add then after
the last commit in origin/masterSo that:
Becomes:
As such, origin will accept the commits that Alice wants to push to the
master branch. Run the following command:
cd ~/git-alice
git rebase origin/master
Will output:
CONFLICT (content): Merge conflict in script.sh
error: could not apply 77181cc... Translate into french
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 77181cc... Translate into french
If only Bob and Alice had been working in different files, we wouldn’t have this conflict. But they have, let’s deal with it! Run the following command to figure WTH is git thinking right now:
git status
interactive rebase in progress; onto 59193ae
Last command done (1 command done):
   pick 77181cc Translate into french
No commands remaining.
You are currently rebasing branch 'master' on '59193ae'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)
Unmerged paths:
  (use "git restore --staged <file>..." to unstage)
  (use "git add <file>..." to mark resolution)
        both modified:   script.sh
no changes added to commit (use "git add" and/or "git commit -a")
In the first line, git says that it is in “rebase mode” with interactive rebase in progress. To cancel, you’d have to do the abort command, but
that’s not what we’re going to do here.
Git is basically saying “I am trying to apply Alice’s second patch on top of
origin/master’s HEAD but you both modified script.sh and I don’t know what
final version you want!”.
Indeed:
git show 59193ae
Shows that this patch removes an echo hello line and add an echo hello world after that:
commit 59193aed08fd896b2c39b6778ef928faffc888a6 (HEAD, origin/master)
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:37:45 2023 +0100
    Completed hello world script
diff --git a/script.sh b/script.sh
index 2f08be9..7c0dffd 100644
--- a/script.sh
+++ b/script.sh
@@ -1 +1 @@
-echo hello
+echo hello world
But as we know, in origin/master, script.sh does not contain echo hello
anymore, but echo hello world, that’s why git doesn’t know how to apply the
patch automatically.
Fortunnately, git has left both versions of the code in the file:
cat script.sh
Will output:
<<<<<<< HEAD
echo hello world
=======
echo bonjour
>>>>>>> 77181cc (Translate into french)
Let’s see what it means line by line:
<<<<<<< HEAD indicates the start of a block of code corresponding to the
HEAD we are rebasing on: Bob’s last commit on origin/masterecho hello world the line that git found when looking for echo hello: it
was changed by Bob on origin/master======= indicates the end of the block of codeecho bonjour is the block of code that git is trying to add>>>>>>> 77181cc (Translate into french) indicates the end of the block of
code that we are rebasing: Alice’s last commitLet’s think about what happened to figure what we need to do:
Translating “hello” to “bonjour” made perfect sense. But translating “hello world” to “bonjour” is ackward, I guess Bob’s manager told him to add “world” and Alice’s manager told her to translate in French.
To have both managers happy, Alice will translate “hello world” in French:
cd ~/git-alice
echo echo bonjour monde > script.sh
Stage this change for commit:
git add script.sh
Run again:
git status
Output:
interactive rebase in progress; onto 59193ae
Last command done (1 command done):
   pick 77181cc Translate into french
No commands remaining.
You are currently rebasing branch 'master' on '59193ae'.
  (all conflicts fixed: run "git rebase --continue")
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   script.sh
As you can see, it has now staged the change in script.sh, there is no
conflict remaining, we can move on with the following command:
git rebase --continue
Output:
[detached HEAD ad0e1cd] Translate into french
 1 file changed, 1 insertion(+), 1 deletion(-)
Successfully rebased and updated refs/heads/master.
Let’s see what changed:
git log
Output:
commit ad0e1cdf3ad490cf4d7561075e17c292f607172a (HEAD -> master)
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:38:08 2023 +0100
    Translate into french
commit 59193aed08fd896b2c39b6778ef928faffc888a6 (origin/master)
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:37:45 2023 +0100
    Completed hello world script
commit ec27700523fc4cdae37de6156ecf1784da24460b
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:36:26 2023 +0100
    Added hello world script
As you can see, the new ad0e1cd commit is still only in our local master
branch, the origin/master still points to 59193ae that Bob pushed.
Most importantly, we see that the “Translate into french” commit that used to
have hash 77181cc now has hash ad0e1cd.
Git tries hard not to loose anything, so we can still inspect both commits individually, as the following will proove:
git show 77181cc
Outputs our old patch:
commit 77181ccf6a2cc6663a854d718099201fd5f78f40
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:38:08 2023 +0100
    Translate into french
diff --git a/script.sh b/script.sh
index 2f08be9..f6cc6aa 100644
--- a/script.sh
+++ b/script.sh
@@ -1 +1 @@
-echo hello
+echo bonjour
And:
git show ad0e1cd
Shows the new version of our commit:
commit ad0e1cdf3ad490cf4d7561075e17c292f607172a (HEAD -> master)
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:38:08 2023 +0100
    Translate into french
diff --git a/script.sh b/script.sh
index 7c0dffd..9844877 100644
--- a/script.sh
+++ b/script.sh
@@ -1 +1 @@
-echo hello world
+echo bonjour monde
You can always get back to a commit you were on your working copy, to find the different commits you have been pointing too, run:
git reflog
Will output:
ad0e1cd (HEAD -> master, origin/master) HEAD@{0}: rebase (continue) (finish): returning to refs/heads/master
ad0e1cd (HEAD -> master, origin/master) HEAD@{1}: rebase (continue): Translate into french
59193ae HEAD@{2}: rebase (start): checkout origin/master
77181cc HEAD@{3}: commit: Translate into french
ec27700 HEAD@{4}: commit (initial): Added hello world script
As you can see, the old version of the commit is still there in memory: git tries hard not to loose commits.
Anyway, now that Alice has rewritten history to comply with the changes that
had been done at the same time in origin, she can go ahead and push:
git push origin master
Will output a successful message:
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 284 bytes | 284.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To /home/jpic/git-central
   59193ae..ad0e1cd  master -> master
This means that on origin, the master branch which used to refer to
59193ae now refers to ad0e1cd.
Obviously, that’s not the sanest way of working as a team. Surely, there are
times were conflicts are inevitable, for example if you let a branch sit a long
time without merging: new commits being merged on origin/master in the future
may cause new conflicts. This is why we need to merge as often as possible:
it’s the “Continuous Integration” practice (not talking about a tool here
but about the practice, two different things!).
git reset --hardBob has learned Alice’s story last month at the afterwork at the pub, and they both figured that it would be more efficient to make sure git is up to date prior to starting any work.
And Bob has not touched this repository for a month! He needs his repository updated and ready to work, so let’s start by fetching upstream changes:
cd ~/git-bob
git fetch
Will output that local git has learned about the new commits on origin/master:
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 264 bytes | 66.00 KiB/s, done.
From /home/jpic/git-central
   59193ae..ad0e1cd  master     -> origin/master
One way of updating the local branch is running git reset --hard, this will
discard any local changes and apply a given ref locally:
git reset --hard origin/master
Will output:
HEAD is now at ad0e1cd Translate into french
Now, Bob’s local master is up to date with origin/master, this means that
Bob can add a commit and push it without having the remote reject it, unless
someone pushes on origin/master meanwhile. Nonetheless, all commits that have
been pushed since he last touched the repository are there now.
Let’s imagine Bob is now working on a complex feature, and Bob learned that git will try hard not to loose any commit, so Bob is going to commit every change he does while iterating on the feature.
cd ~/git-bob
cat <<EOF > script.sh
msg="bonjour monde"
echo \$msg
EOF
git add script.sh
git commit -m "msg variable"
cat <<EOF > script.sh
msg="\${msg-bonjour monde}"
echo \$msg
EOF
git add script.sh
git commit -m "Let environment override msg variable"
cat <<EOF > script.sh
message="\${message-bonjour monde}"
echo \$msg
EOF
git add script.sh
git commit -m "Let's not use short variable names"
cat <<EOF > script.sh
message="\${message-bonjour monde}"
echo \$message
EOF
git add script.sh
git commit -m "Fix typo"
As you can see, Bob now has 4 commits in his master on top of
origin/master:
git log
Output:
commit 2189ab7b028b8f017aec1eb3f42c7e152009119e (HEAD -> master)
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 13:01:27 2023 +0100
    Fix typo
commit 594f691ab9e8f1194b42c0fe5e56a10f75ac7432
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 13:01:27 2023 +0100
    Let's not use short variable names
commit d1981e1b765a8eed044f49a4525e5235456769bc
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 13:01:27 2023 +0100
    Let environment override msg variable
commit 79f5352bdec7af315e1423ee37e104473c879293
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 13:01:26 2023 +0100
    msg variable
That’s a lot of what we call “work commit”, those are commits that were made while working.
But that’s not commits we want to keep in the mainline history: we want
“atomic” commits that add a single feature change, this will make it easier in
the future when we need to introspect the past commits to figure why a
particular change was made, and also make it easier to git-revert a feature.
To squash all commits into one, run git-rebase in interactive mode:
git rebase -i origin/master
This will open an editor with:
pick 79f5352 msg variable
pick d1981e1 Let environment override msg variable
pick 594f691 Let's not use short variable names
pick 2189ab7 Fix typo
# Rebase ad0e1cd..2189ab7 onto ad0e1cd (4 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
First, we see our 4 commits, and before each commit we see the rebase command.
The commands are described in the comment below. We’ll use the fixup command
to squash last 3 commits into the first one, and the reword command on the
first commit to enhance the message, make the following changes:
r 79f5352 msg variable
f d1981e1 Let environment override msg variable
f 594f691 Let's not use short variable names
f 2189ab7 Fix typo
Save and quit the editor, git will open an editor again with the first commit message that we told it we wanted to reword:
msg variable
Given that we’re squashing all the following commits into that one, we want this commit message to be a full description of what it will contain, change to:
Now print the new overridable $message variable, or "bonjour monde"
Save and close the editor. Git outputs:
[detached HEAD 8222fe9] Now print the new overridable $message variable, or "bonjour monde"
 Date: Mon Mar 13 13:01:26 2023 +0100
 1 file changed, 2 insertions(+), 1 deletion(-)
Successfully rebased and updated refs/heads/master.
Let’s see what our new commit looks like:
git show
Output:
commit d2ffb10d408fb27d6843a2db2c548b95686596a2 (HEAD -> master)
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 13:01:26 2023 +0100
    Now print the new overridable $message variable, or "bonjour monde"
diff --git a/script.sh b/script.sh
index 9844877..a3f1278 100644
--- a/script.sh
+++ b/script.sh
@@ -1 +1,2 @@
-bonjour monde
+message="${message-bonjour monde}"
+echo $message
Pretty clean! Also, with rebase we have rewritten history again as you can see with:
git log
Output:
commit d2ffb10d408fb27d6843a2db2c548b95686596a2 (HEAD -> master)
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 13:01:26 2023 +0100
    Now print the new overridable $message variable, or "bonjour monde"
commit ad0e1cdf3ad490cf4d7561075e17c292f607172a (origin/master, origin/HEAD)
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:38:08 2023 +0100
    Translate into french
commit 59193aed08fd896b2c39b6778ef928faffc888a6
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:37:45 2023 +0100
    Completed hello world script
commit ec27700523fc4cdae37de6156ecf1784da24460b
Author: James Pic <james.pic@external.thalesgroup.com>
Date:   Mon Mar 13 11:36:26 2023 +0100
    Added hello world script
Note that git hasn’t lost any of our “work commits”, we can still find them
with git reflog!
Normally, you wouldn’t be doing that on master but rather in branches. As we like to say in git “branches are cheap!”
Create a branch with:
git checkout -b branchname origin/master
Output:
Branch 'branchname' set up to track remote branch 'master' from 'origin'.
Switched to a new branch 'branchname'
You can see that git knows it’s on branchname:
git status
Output:
On branch branchname
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
Let’s create an empty commit in that branch for the sake of testing:
git commit --allow-empty -m test
Output:
[branchname 847d5eb] test
You can switch back to your local master branch too:
git checkout master
As you can see, the test commit that is on the branchname branch is not
present:
git log
Let’s move again to our branchname branch:
git checkout branchname
And see our test commit:
git log
You can push a branch onto another, for example, I want to push my local
master branch to a new featurefoo branch on the origin remote:
git push origin master:featurefoo
Output:
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 344 bytes | 344.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To /home/jpic/git-central
 * [new branch]      master -> featurefoo
If you want to delete the featurefoo branch from the origin, then push
“nothing” onto it:
git push origin :featurefoo
Output:
To /home/jpic/git-central
 - [deleted]         featurefoo
If your git is recent enough, you can also use the git worktree feature which
is pretty cool, read the docs with man git-worktree.
Usually, you’re going be working in branches, pushing your branch to a git server, waiting for Continuous Integration to report its result and for your colleagues to review your code.
Then, you’re going to push more “work commits” to that branch, either to fix CI or to add changes that your colleagues requested.
When the branch is ready, this is when you are going to squash all commits into one, if your branch represents a single feature change, which it should, when possible.
But when you squash your commits locally, git will again complain about the
work commits that are on branchname on origin, commits that you have
deleted when rewritting history to be clean.
In this case, you will be able to push with --force, telling git “yes, I know
what I’m doing”.
The entire “Pro Git book” written by Scott Chacon and Ben Straub and published by Apress, is available here. All content is licensed under the Creative Commons Attribution Non Commercial Share Alike 3.0 license.
Recommended chapters after going through these tutorials are: