Wiki copymirror

This is a mirror of the wiki at pascal.chat. Editing, history and sign-in are there.

GIT Exercises

This page has some exercises so you can experience different solutions/outcomes for a given task.

By providing these (partly trivial) examples, the page ensures that you will not miss any of the scenarios while discovering the wonders of GIT.

Preface

Some of the examples are about how to deal with your work when other people are pushed in between.

For this you need a 2nd clone. See setup.

Setup

The following steps should be performed before going to any of the below items.

Initial setup and clone

SVN_to_GIT_Cheatsheet#Setup

Second clone

To simulate what someone else would have done while you where working. This can use the same user, the effect will be the same.

You can either "git clone" again, or just make a copy of the entire directory into which you have cloned.

You can then in that 2nd clone alter your username

text
   git config user.name  martin_2

Each example setup

In your first git folders, create a new branch

text
  git switch -c martins-test-1
  git push --set-upstream origin martins-test-1  ## tell the server there is a new branch

In your 2nd git folder

text
  git remote update
  git switch martins-test-1

Thus both your folders are at the same branch, and are up to date with the remote

Note: In the example you work on "martins-test-1". But it is setup the same as your trunk branch would be. Therefore the same will apply when you work on trunk

Pushing your commit, if someone else made changes

Note: If you followed the configuration your "git pull" should be configured to default to either "--ff-ony" or "--rebase".
The arguments given in to "pull" in the examples, will override the defaults

Rebase

In Folder1

In Folder2 (other person)

Setup new branches #Each example setup

| Edit the file lcl/forms.pp

text
   git commit -m 'test changes' lcl/forms.pp
text
   git log --oneline --graph --all -n 2
      * 3b007fdc38 (HEAD -> martins-test-1) test changes
      * c739dd68a7 (origin/master, origin/HEAD) Revert "Test a change in forms"

Your commit, follows the previous commit

|

|

| Edit the file ide/main.pp

text
   git commit -m 'test other' ide/main.pp
   git push

|

text
   git push
       ! [rejected]              martins-test-1 -> martins-test-1 (fetch first)
      error: failed to push some refs to 'https://gitlab.com/freepascal.org/lazarus_testconversion.git'

Instead of the normal pull, we do a fetch. This will not resolve the problem, but we can see what it looks like.

text
   git fetch
text
   git log --oneline --graph --all -n 3
      * 46a817b6b3 (origin/martins-test-1) test other
      | * 3b007fdc38 (HEAD -> martins-test-1) test changes
      |/
      | * c739dd68a7 (origin/master, origin/HEAD) Revert "Test a change in forms"

|

For the #Merge example stop here, and continue in the next section

| You can see your commit still follows the same commit as before.

But on the server (origin/martins-test-1) there is the other commit, and it too has the same parent. The history on the server is fixed. We do not allow for it to be changed. So that commit will keep its parent.

In order to get your commit to the server, your local commit must change it's parent to "test other" (this is called "rebase")

text
   git pull --rebase
      Successfully rebased and updated refs/heads/martins-test-1.
text
   git log --oneline --graph --all -n 3
      * 2cbe840e7e (HEAD -> martins-test-1) test changes
      * 46a817b6b3 (origin/martins-test-1) test changes
      * c739dd68a7 (origin/master, origin/HEAD) Revert "Test a change in forms"

Now your commit is on top, and we have a flat line of commits (like svn)

text
    git push

|

Merge

Warning: This example is what we want to avoid. This creates a diverged history (sub-branches). The log is no longer linear.

In Folder1

In Folder2 (other person)

Setup new branches #Each example setup

Repeat the steps from #Rebase above until you have done "git fetch" and reached the note for the "merge example"

|

Now run

text
   git pull --no-ff --no-rebase
      Merge made by the 'recursive' strategy.
       ide/main.pp | 3 ---
       1 file changed, 3 deletions(-)

The --no-ff --no-rebase overrides any configs you may have made, and gets you the default behaviour of merging. Git will open an editor for you, and ask for a commit message for the merge. The merge creates a new commit.

text
   git log --oneline --graph --all -n 4
      *   044d9288b7 (HEAD -> martins-test-2) Merge branch 'martins-test-2' of https://gitlab.com/freepascal.org/lazarus_testconversion into martins-test-2
      |\
      | * 379b633c88 (origin/martins-test-2) test other
      * | cfa3def67c test changes
      |/
      * 2cbe840e7e foo bar

As you can see the two commits are in different sub-branches (they are actually side by side). And a new merge commit has been added.

You can push this.

But this creates a commit log, like you would never see in svn.

|

Fast Forward

Note: In this example you will not have made a commit. Only the other person will have made a commit

Fast-Forward (FF) can only happen if

In Folder1

In Folder2 (other person)

Setup new branches #Each example setup

| Edit the file lcl/forms.pp Do not commit the changes.

text
   git status -uno
      On branch martins-test-1
      Your branch is up to date with 'origin/martins-test-1'.
      
      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:   lcl/forms.pp

Your changes are relative to the last commit

text
   git log --oneline -n1
      c739dd68a7 (HEAD -> martins-test-1) Foo bar"

|

|

| Edit the file ide/main.pp

text
   git commit -m 'test other' ide/main.pp
   git push

| Fetch the data from the remote

text
   git fetch

Status will tell you you are behind the remote

text
   git status -uno
      On branch martins-test-1
      Your branch is behind 'origin/martins-test-1' by 1 commit, and can be fast-forwarded.
        (use "git pull" to update your local branch)
      
      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:   lcl/forms.pp

Your changes are still relative to the last local commit. Indicated by "HEAD"

text
   git log --oneline -n2 origin/martins-test-1
      bc20668e78 (origin/martins-test-1) test other
      c739dd68a7 (HEAD -> martins-test-1) Foo bar"
text
   git pull --ff-only
      Updating c739dd68a7..bc20668e78
      Fast-forward
       ide/main.pp | 2 ++
       1 file changed, 2 insertions(+)

Now the local branch "martins-test-1" has caught up to the remote branch. It includes the new commits. And it switched to the top commit.
This catching up, is a fast forward.

text
   git log --oneline -n2 --graph origin/martins-test-1
      bc20668e78 (HEAD -> martins-test-1, origin/martins-test-1) test other
      c739dd68a7 Foo bar"

|

Conflict Resolution

Getting selected upstream changes

In Folder1

In Folder2 (other person)

Setup new branches #Each example setup

| Edit the file lcl/forms.pp
Edit the file ide/main.pp somewhere from line 100 down

text
   git diff --stat
      ide/main.pp  | 5 +++++
      lcl/forms.pp | 4 ++++
      2 files changed, 9 insertions(+)
text
   git log --oneline -n 1
      c739dd68a7 (HEAD -> martins-test-1) Foo Bar"

|

|

| Edit the file ide/main.pp somewere in the first 20 lines (no conflict with the main user in folder1)
Also edit the file lcl/grids.pas

text
   git commit -m 'test other' -a
   git push

| Update from remote. Keep current commit as base for your changes

text
   git fetch
   git log --oneline -n2  origin/martins-test-1
      810b4f1bd4 (origin/martins-test-1) test other
      c739dd68a7 (HEAD -> martins-test-1) Foo Bar

The commit "test other" changes several files. For testing you want to integrate the changes in file ide/main.pp, but no other changes.

text
   git stash
   git restore --source=origin/martins-test-1  ide/main.pp
   git commit -m 'dummy commit'  ide/main.pp
   git stash pop

Now you have a dummy commit with the changes to test. And your changes applied on top.

text
   git log --oneline -n2
      63e2dcec9f (HEAD -> martins-test-1) dummy commit
      c739dd68a7 Foo Bar
   git diff --stat
      ide/main.pp  | 5 +++++
      lcl/forms.pp | 4 ++++
      2 files changed, 9 insertions(+)

Now work with those changes, make further changes, eventually commit

text
   git commit -m 'my changes' -a
      [martins-test-1 9aba7218d1] my changes
       2 files changed, 9 insertions(+)
text
   git log --oneline -n5 --graph --all
      * 9aba7218d1 (HEAD -> martins-test-1) my changes
      * 63e2dcec9f dummy commit
      | * 810b4f1bd4 (origin/martins-test-1) test other
      |/
      * c739dd68a7 Foo Bar

So you now need to rebase your changes on the origin/martins-test-1 to commit them.

text
   git pull --rebase
      Successfully rebased and updated refs/heads/martins-test-1.
text
   git log --oneline -n3
      f0d6afd90d (HEAD -> martins-test-1) my changes
      810b4f1bd4 (origin/martins-test-1) test other
      c739dd68a7 Foo Bar

The "dummy commit" is gone. Because it was identical to existing changes. Of course you should check this. If it is still there you need to drop it with

text
   git rebase -i

|


From the Free Pascal wiki, page "GIT Exercises", as it stood on 2021-12-29 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 3, last changed by WikiArchive .