Friday, April 6, 2018

Introduction to GIT

Content:

  1. Getting a Git Repository
  2.     1.1 How to get a Git repository
        1.2 Initializing a Repository in an Existing Directory
        1.3 Cloning an Existing Repository
  3. Recording Changes to the Repository
  4.     2.1 Recording Changes to the Repository
        2.2 Checking the Status of Your Files
        2.3 Adding a new file to your projecty
        2.4 Tracking New Files
        2.5 Staging Modified Files
        2.6 Ignoring Files
        2.7 Viewing Your Staged and Unstaged Changes
        2.8 Committing Your Changes
        2.9 Removing Files
        2.10 Moving Files
  5. Viewing the Commit History
  6.     3.1 Viewing the Commit History
  7. Working with Remotes
  8.     4.1 Working with Remotes
        4.2 Showing Your Remotes
        4.3 Adding Remote Repositories
        4.4 Fetching and Pulling from Your Remotes
        4.5 Pushing to Your Remotes
        4.6 Inspecting a Remote
        4.7 Renaming and Removing Remotes
  9. Branching
  10. Rewriting History
  11. Configuration

0 Getting Started

Git differs from other VCS in that it makes snapshots not differences

Git stores and thinks about information in a very different way than other VCSs.

  • Other version control systems store informations as a set of base files and the changes made to each file over time, this is commonly described as delta-based version control. Instead, Git stores the data as series of snapshots of a filesystem.
  • To store the state of the your project, Git makes a copy, or snapshot, all the files of the project and save them. To be more efficient, if a file has not changed, Git doesn’t store the file again but a link to the already saved file in a previous snapshot.
  • This is an important distinction between Git and all other VCSs, that makes Git more like a mini filesystem. This have many benefits when branching a Git project.

1 Getting a Git Repository

This guide contains the basic commands required to work with Git.

How to get a Git repository

You can get a Git repository in two ways: 1 by turning a local directory into a local Git repository, 2 by getting a copy of an existing remote Git repository.

Initializing a Repository in an Existing Directory

You have a project directory, not under version control, you want to control with Git: go to the directory of the project and type: git init
The git init command create a .git subdirectory that contains the files required by the Git repository.

Cloning an Existing Repository

To get a copy of an existing Git repository, you use the clone command. The clone command pulls down (also said check out) a working copy of the latest version of the remote repository.
(This is often done when you want to work on a project that's hosted on a platform like GitHub, GitLab or Bitbucket).

Example, clone the Oracle's First Cup Java Tutorial

## clone the First Cup Java Tutorial in the 'firstcup-examples' directory
$ git clone https://github.com/javaee/firstcup-examples
## clone the First Cup Java Tutorial in a target directory of your choice
$ git clone https://github.com/javaee/firstcup-examples firstCupJavaTutorial

2 Recording Changes to the Repository

Recording Changes to the Repository

Now, you want to make change to files and commit snapshots into your repository.

Files in your working directory can be in two states: traked and untraked.

  • Traked files are files that were in the last snapshot.
    • traked files can be: unmodified, modified and staged
    • when you clone a repository, the files in your working directory are all traked and unmodified
  • Untracked files are files in your working directory that were neither in the last snapshot nor in the Staging area.
    • a file just created is in the untracked state

What is meant for working tree?

The term "working tree" refers to the directory on your file system where you can edit your files.

  1. Definition: The working tree is the set of files and directories of your project that you commit to and check out from the Git repository.

  2. Functionality: The working tree is where all the action happens in your Git workflow. In the working tree, you can add, edit, rename and delete files. Changes made in the working tree can be staged and committed to the repository. The working tree reflects the current state of your project, showing both tracked and untracked files.

  3. Note: The working tree exists alongside the Git directory, which is hidden and usually named .git. The Git directory contains the repository's metadata and version history.

  4. Note: Git allows you to have multiple working trees associated with a single repository. This means you can check out different branches simultaneously, which is useful for working on multiple features or fixes at the same time.

What is meant for Working Directory?

  1. Definition: The working directory refers to the current directory in which your shell or command line is operating, which may or may not be the same as the working tree.
  2. Note: while they are closely related, the working tree is more about the files and their state in the context of Git, whereas the working directory is about your current location in the file system.
Checking the Status of Your Files

The git status command allows you to determine in which state your files are.

If you run the git status command after a clone, you should see an output like the one below:

user@host:~$ cd firstcup-examples/
user@host:~/firstcup-examples$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
user@host:~/firstcup-examples$

The message "nothing to commit, working tree clean" indicates a specific state of your repository:

  1. Nothing to Commit: tells you that there are no changes in your working directory that need to be saved to the repository.

  2. Working Tree Clean: indicates that your working directory is in a clean state. It means that: 1. All tracked files are unmodified. 2. There are no untracked files. 3. Your local branch is up to date with the remote branch (if you are working with a remote repository).

The Git status command also tell you in which branch you are on: master is the default branch.

Adding a new file to your project

Add a new file, the README file, to your project and run the git status command to see what has happened.

user@host:~$ cd firstcup-examples/
user@host:~/firstcup-examples$ vim README
user@host:~/firstcup-examples$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Untracked files:
  (use "git add ..." to include in what will be committed)

 README

nothing added to commit but untracked files present (use "git add" to track)
user@host:~/firstcup-examples$

The git status command shows the README file as untracked.

Tracking New Files

To begin traking a new file, you use the git add command.

user@host:~/firstcup-examples$ git add README

Run the git status command, again. The README file now is traked and staged.

user@host:~/firstcup-examples$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD ..." to unstage)

 new file:   README

user@host:~/firstcup-examples$
Staging Modified Files

Modify the CONTRIBUTING.md file and run the git status command.

user@host:~/firstcup-examples$ vim CONTRIBUTING.md
user@host:~/firstcup-examples$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD ..." to unstage)

 new file:   README

Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working directory)

 modified:   CONTRIBUTING.md

user@host:~/firstcup-examples$

The modified file appears under the “Changes not staged for commit” section.

  • run the git add CONTRIBUTING.md
  • run the git status command again
user@host:~/firstcup-examples$ git add CONTRIBUTING.md
user@host:~/firstcup-examples$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD ..." to unstage)

 modified:   CONTRIBUTING.md
 new file:   README

user@host:~/firstcup-examples$ 

Both files are staged

The git add command is used to:

  • to start traking new files
  • to stage modified files
  • to mark merge conflict as resolved

If you modify the CONTRIBUTING.md file again, the file will appear both in the staged files and in the unstaged files.

$ vim CONTRIBUTING.md
$ git status

On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD ..." to unstage)

    new file:   README
    modified:   CONTRIBUTING.md

Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

Every time you modifiy a staged file, you have to run git add to stage the latest version of the file.

Ignoring Files

Suppose you have a class of file you don't want Git to automatically add to staging area or show as being untraked.

  • you can create a file named .gitignore with patterns of names of files that are to be ignored.

Below a sample .gitignore file for java projects.

# Compiled class file
*.class

# Log file
*.log

# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid* 

The first line tells Git to ignore any files ending in “.class” — class files that are the product of building your code. The second line tells Git to ignore log files, etc.

The rules for patterns in the .gitignore file use standard glob patterns that shells employ.

Viewing Your Staged and Unstaged Changes

The git diff command shows you what has changed, but is more accurate than the status command.

  • whereas the status command shows you which files are changed, the diff command shows you what is changed
  • the diff command shows you the patch: the lines of code added and removed.
  • use the git diff command to see:
    • what you have changed but not yet staged
    • what you have staged and you are going to commit

The command git diff without arguments shows you what you have changed but not yet staged

  • the command compares what is your working directory and what is in your staging area.

The command git diff --staged (or git diff --cached) shows you the changes you have staged and are going to be committed

  • this command compares the changes you have staged to your last commit
Committing Your Changes

When you want to take a snapshot of your project, commit your changes.

The simplest way to commit is to type the git commit command:

  • this launches the editor you configured with the git config --global core.editor command
  • the editor displays a default message:

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# Your branch is up-to-date with 'origin/master'.
#
# Changes to be committed:
#       modified:   CONTRIBUTING.md
#       new file:   README
#
~                         
~ 

The default message contains an empty line and the latest output of the git status command. If you run the command git commit -v , the -v option put the diff of your change in the commit message.

Alternatively, you can use the commit command with the -m flag and pass an inline message for the commit

  • for example: $ git commit -m "fix the xxx bug in the YYY file"

A commit records a snapshot of the changes you prepared in the staging area. Things in the working directory you didn't stage do not take part in the commit. You can always revert to or compare your project to a snapshot you previously recorded.

Removing Files

To remove a file from the git repository run the git rm <file> command

  • the git rm <file> command delete the file from working directory and staging area
  • after using the rm command, you have to commit the changes

The bash rm <file> command only remove the file from the working directory

How to make Git stop to trak a file and keep it in your working directory

  • to mark a file as untraked, it means the file is removed from the staging area
  • to stop traking a file run the git rm --cached <file> command
Moving Files

To rename a file in git use the git command: git mv <file-from> <file-to>

3 Viewing the Commit History

Viewing the Commit History

If you want to see the history of all the commits on a repository, use the git log command

The git log command (used without arguments) lists the commits made in a repository from the most recent to the oldest ones.

  • for each commit, it shows checksum, author, date and message
user@host:~/firstcup-examples$ git log
commit 840c16f6fe3c664f0f066de0856fed994892cbe2 (HEAD -> master, origin/master, origin/HEAD)
Author: Ankur Kathuria 
Date:   Fri Jan 25 15:50:07 2019 +0530

    Update CONTRIBUTING.md

commit 2cf9f53fb0657883e65a35f470504f515489dcc9
Author: Ankur Kathuria 
Date:   Fri Jan 25 15:49:50 2019 +0530

    Update README.md

commit 81340757a13c03a9f602c6665a4eb6bf84803c10
Author: Sameer Pandit 
Date:   Wed Sep 20 11:06:07 2017 +0530

    [maven-release-plugin] prepare for next development iteration

commit 0c8a6541ed196128f5a874dd0e7b5ca97ca6b623 (tag: 8.0)
Author: Sameer Pandit 
Date:   Wed Sep 20 11:05:58 2017 +0530

    [maven-release-plugin] prepare release 8.0

The git log command options:

  • the option -p or --patch shows the difference or patch for each commit
  • the option --pretty=<format> produces different output according to the format passed. For example, git log --pretty=oneline or git log --oneline produces a single line information; other formats are --pretty=short, --pretty=full and --pretty=fuller
  • the option --graph adds an ASCII graph showing branch and merge history

4 Working with Remotes

Working with Remotes

What is a remote repository?

  • a remote repository is a version of a project that is hosted on a machine connected to the internet or network.
  • a Git project can have multiple remote repositories, each one is read-only or read/write.
  • if you want to collaborate on a Git project, you must know how to handle remote repositories

Syncing Git repositories

  • Git has a centralized repository, which works as hub for all the developers
  • every developer has its own copy of the repository;
  • developers share commits between repositories

Working with a remote repository involves:

  • adding new repositories and removing invalid repositories
  • pushing and pulling data to and from repositories
  • managing different branches

Git Remote Connections

  • a remote connection is like a bookmark that maps a name to a URL
  • the configuration file ./.git/config keeps a list of remote connection entries

Example: a Git repo with two remote connections

    the following diagram shows your repo with two remote connections: a first connection between your repository and the central repository and a second connection between your repository and another developer's repository

The git remote command

  • this command allows you to view, create and delete connections to other repositories
  • this command is an interface for handling the list of remote connection entries held in the ./.git/config file
Showing Your Remotes
git remote
git remote -v or --verbose
  • the git remote command lists the names of the remote servers that have been configured. If your repository was cloned, this prints origin.
  • the -v option lists name and URL for each remote repository.

Example of git remote command

user@host:~$ git clone https://github.com/javaee/firstcup-examples
Cloning into 'firstcup-examples'...
user@host:~$ cd firstcup-examples/
user@host:~/firstcup-examples$ git remote
origin

# invoke with -v verbose option
user@host:~/firstcup-examples$ git remote -v
origin https://github.com/javaee/firstcup-examples (fetch)
origin https://github.com/javaee/firstcup-examples (push)

Repository with multiple remotes

    a repository with multiple remotes allows to work with many collaborators: you pull contributions from any remotes, and you may additionaly have permission to push your commits

Example of git remote -v command on a repository with multiple remotes

$ cd grit
$ git remote -v
origin     git@github.com:mojombo/grit.git (fetch)
origin     git@github.com:mojombo/grit.git (push)
backstab   https://github.com/backstab/grit (fetch)
backstab   https://github.com/backstab/grit (push)
cho45      https://github.com/cho45/grit (fetch)
cho45      https://github.com/cho45/grit (push)
merten     https://github.com/merten/grit (fetch)
merten     https://github.com/merten/grit (push)
softdrink  git://github.com/softdrink/grit.git (fetch)
softdrink  git://github.com/softdrink/grit.git (push)

Example git remote command on a forking repository

$ git remote
origin
upstream
collaborator_repo

# invoke with -v verbose option
$ git remote -v
origin               https://github.com/YOUR_USERNAME/YOUR_FORK.git (fetch)
origin               https://github.com/YOUR_USERNAME/YOUR_FORK.git (push)
upstream             https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git (fetch)
upstream             https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git (push)
collaborator_repo    https://github.com/COLLABORATOR_USERNAME/REPONAME.git (fetch)
collaborator_repo    https://github.com/COLLABORATOR_USERNAME/REPONAME.git (push)
Adding Remote Repositories
# create a new connection to a remote repository
git remote add <remote_repo_name> <remote_repo_url>

The git remote add command configures a new remote repository, by associating a name with a remote URL

  • you can reference the remote you added by its name
  • you can use the remote's name in the command line, while executing commands to pull contributions

The origin Remote

  • when you clone a repository, Git automatically adds a remote connection named origin which points back to the cloned repository. This is why many Git-based projects refer to their central repository as origin
  • the origin repository allows developers to pull down upstream changes and publish local commits

Git can express remote repository URLs in two ways:

  • via the HTTP protocol, to allow anonymous read-only access to the repository, for example: http://host/path/to/repo.git
  • via the SSH protocol, to allow authenticated read-write access to the repository, for example: ssh://user@host/path/to/repo.git

Git-based projects can have other remotes in addition to origin

  • small team can add connections to teammates's repositories to collaborate outside the central repo
  • in the context of GitHub forks, you fork a GitHub repo and then you clone that fork on your local machine. In that case, your repo will have two remote connections al least:
    • the upstream remote connection that refers to the original repository that you have forked
    • the origin remote connection that refers to your own repo on GitHub, the fork of the original repository
Fetching and Pulling from Your Remotes

Run a fetch to see what other developers are working on

# fetch all the branches from the specified remote repository
git fetch <remote>

The git fetch command:

  • it allows to download commits, files and commit references from a remote repository into your local repository
  • it does not modify or mess up anything in the current working directory: fetched content is isolated from local content
  • it does not force you to merge the changes into your repository,
    • after a fetch, you have to merge the downloaded data into your working directory manually

How does Git organize and store commits?

  • git stores local and remote commits in the ./.git/objects directory of the repository
  • git keeps local commits separated from remote commits through the use of branch refs
    • the refs for local branches are stored in the ./.git/refs/heads/ directory
    • the refs for remote branches lives in the ./.git/refs/remotes/ directory
  • the command git branch prints a list of local branch refs
  • # list local branch refs
    user@host:~/heroku-getting-started$ git branch
    * master
      feature55
      testing  
    # if you list the files held in the directory, 
    # you get the same output 
    $ ls ./.git/refs/heads/
    master  feature55  testing    
    
  • remote branches are the same of local branches, except that they map to commits from other developer's repositories
  • you can check out remote branches just like local ones, but afterward you are in detached HEAD state
  • remote branches are prefixed by the name of the remote they belong to
  • the command git branch -r lists remote-tracking branches refs
  • # lists remote-tracking branches refs
    user@host:~/heroku-getting-started$ git branch -r
      heroku/master
      origin/HEAD -> origin/master
      origin/app-json
      origin/docs
      origin/localchanges
      origin/main
      origin/master
      ...
    
  • the command git branch -a list both local and remote branch refs
  • # list all branch refs
    user@host:~/heroku-getting-started$ git branch -a
    * master
      feature55
      remotes/heroku/master
      remotes/origin/HEAD -> origin/master
      remotes/origin/app-json
      remotes/origin/docs
      remotes/origin/localchanges
      remotes/origin/main
      remotes/origin/master
      ...
    
  • remote branches are prefixed by the remotes/<remote> repo names

Example: synchronize with central repository

  • suppose that your local repository refers to its central repository as origin and the central repository's default branch is named master
git fetch origin  # download commits of all the branches from origin remote 
git log --oneline master..origin/master  # inspect the log of changes on upstream master 
git checkout master  # update index and the file in your working tree 
git log origin/master  # see all commits of master branch 
# if you approve the changes, run:
git merge origin/master  # to apply the new commits into your local branch

The git pull command

git pull <remote_name> <branch_name>
  • if your current branch tracks a remote branch, the git pull command fetches data from the remote and tries to merge that remote branch in your current branch.
  • as the git clone command implicitly sets your local master branch to track the master branch on the remote you cloned from, the git pull command fetches data from the origin remote and automatically tries to merge it into your working tree

git pull compared to git fetch

  • the git fetch command downloads the remote content but not updates your local repository's working tree
  • the git pull command downloads the remote content for the active local branch and immediately executes git merge to create a merge commit for the new remote content
  • NOTE: generally, it’s better to use the fetch and merge commands explicitly than the pull command
Pushing to Your Remotes

The git push command allows to upload local repository content to a remote repository.
Fetching imports commits to local branches, whereas pushing exports commits to remote branches.

 # pushes the specified local branch to remote
 git push <remote_repo> <branch> 
  • this pushes all the commits and their internal objects
  • this creates a local branch in the destination repository
  • note: the push command is successful only if nobody has pushed since you cloned the repository. If somebody already pushed to the same branch as you, your push would overwrite their commits and therefore your push is refused with a non-fast-forward merge error. In this case, you have to fetch their work and merge it with yours or simply pull, before being allowed to push

The git push command options

# push the specified local branch even if the result is a non-fast-forward merge
git push <remote> <branch> --force

# push all of your local branches to the specified remote
git push <remote> --all

# send all your local tags to the remote repository
git push <remote> --tags
  • note: tags are not automatically sent when you upload branches

Force Pushing

  • If local repository's branch diverges from remote repository branch, Git prevents you from pushing your commits, you need to pull the remote brancg and merge before pushing
  • the --force flag overrides this behavior and make the remote repository branch be equal to your local branch, by deleting any upstream changes occurred since you pulled
  • the only time you should force push is when you realize that the commits you just shared were not correct and you fixed them with a git commit --amend or git rebase -i. Anyway, you should be sure that no one from your team have pulled those commmits before using the --force option

Example: standard workflow for publishing local commits

# make sure your on the master branch
git checkout master
# make sure your local master is up to date
git fetch origin master
# place your new commits on top of the latest master
git rebase -i origin/master
# upload your commits
git push origin master

Example: amended force push

# make changes to a repo and git add
git commit --amend
# update the existing commit message
git push --force origin master

Example: delete a remote branch

git branch -D branch_name
git push origin :branch_name
Inspecting a Remote

The git remote show <remote> command prints detailed information about the configuration of the specified remote repository

git remote show <remote_name>
  • this command lists the endpoint URLs attached for fetching and pushing, traking branches and how your local branches are configured for the push and pull commands

Examples of the git remote show command:

$ git remote show origin
* remote origin
  Fetch URL: https://github.com/schacon/ticgit
  Push  URL: https://github.com/schacon/ticgit
  HEAD branch: master
  Remote branches:
    master tracked
    dev-branch tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local ref configured for 'git push':
    master pushes to master (up to date)
user@host:~/heroku-serieatim$ git remote show heroku
* remote heroku
  Fetch URL: https://git.heroku.com/serieatim.git
  Push  URL: https://git.heroku.com/serieatim.git
  HEAD branch: master
  Remote branch:
    master tracked
  Local ref configured for 'git push':
    master pushes to master (up to date)
user@host:~/heroku-getting-started$ git remote show origin
* remote origin
  Fetch URL: https://github.com/heroku/java-getting-started
  Push  URL: https://github.com/heroku/java-getting-started
  HEAD branch: main
  Remote branches:
    dependabot/bundler/docs/nokogiri-1.11.5 tracked
    localchanges                            tracked
    main                                    tracked
    refs/remotes/origin/app-json            stale (use 'git remote prune' to remove)
    refs/remotes/origin/docs                stale (use 'git remote prune' to remove)
    refs/remotes/origin/master              stale (use 'git remote prune' to remove)
    refs/remotes/origin/smallbump           stale (use 'git remote prune' to remove)
    refs/remotes/origin/spark-localchanges  stale (use 'git remote prune' to remove)
    refs/remotes/origin/spring-2.1.1        stale (use 'git remote prune' to remove)
    refs/remotes/origin/spring-localchanges stale (use 'git remote prune' to remove)
    refs/remotes/origin/ui                  stale (use 'git remote prune' to remove)
    test-apt-buildpack                      tracked
    war-support                             tracked
  Local branch configured for 'git pull':
    master merges with remote master
Renaming and Removing Remotes
git remote rename <from-remote-name> <to-remote-name> 

the git remote rename command changes a remote's name.

  • when you rename a remote also tracking branches are renamed
git remote remove <remote-name>

The git remote remove command removes a remote.

  • when you remove a remote, you also remove tracking branches with that remote

5 Branching

When you run the git command, Git creates a commit object and stores this object in the Git repository. If you commit again, the following commit stores a reference to the commit that came immediately before it. As you make more commits, the history of your project creates a stream of snapshots in the Git repository.

A branch in Git is simply a movable pointer to one of these commits. When you make the first commit, this creates a branch, which is named by default master. The master branch points to the last commit you made. Every time you commit, it moves forward automatically.

In versioning systems, the term branching is used to indicate that you diverge from the main line of development and continue to work and commit into a different line. In Git, the creation of a new branch implies the creation of a new pointer to commit objects.

If a repository has several branches, Git knows what branch you’re currently on by using a special pointer called HEAD. The HEAD is a pointer to the local branch you’re currently on: usually you are on master.

A Git 'tracking branch' is a local branch that is connected to a remote branch. When you push and pull on that branch, it automatically pushes and pulls to the remote branch that it is connected to

6 Rewriting History

In Git, you can change you local commit history

  • this implies rewriting commits that already happened, so they look like they happend in a different way
  • you can change the order of commits, change the commit message, change files in a commit, compressing together or splitting commits
  • note: with Git, you are free to rewrite your history locally. You should consider pushed work as final and avoid pushing your work until you’re happy with it and willing to share it with the rest of the world
1 Changing the Last Commit

The git commit --amend command allows to modify the last commit

  • you would you want to commit on the last commit
    • to combine the staged changes with the previous commit and not create a new commit
    • to simply edit the previous commit message without changing its content
  • note: the amended commit is not modified but replaced by a new commit with its own ref
  • note: do not amend public commits
    • amending public commits can give rise to confusing situations
    • do not amend a commit if other developers have based their work on them
    • when you amend a commit the previous commit will not appear anymore in your local branch

Example 1: use git commit --amend to change the commit message

git commit --amend
# this opens the old commit message in the text editor
# where you can make changes to the message

git commit --amend -m "updated commit message"
# this updates the old commit message, passing the new message from command line

Example 2: use git commit --amend to change committed files

# edit main.css file to fix bug #33
git add main.css
git commit -m "bug #33 fixed"
# realize the bug is still present, fix it again 
# stage the fixed file 
git add main.css
# execute git commit --amend, to change the previous commit
# the --no-edit flag makes the amendment w/o changing the commit message
git commit --amend --no-edit
2 Changing Multiple Commit Messages

The rebase command allows to rebase a series of commits onto the HEAD they were originally based on, instead of moving them to another one

  • if you want to run the rebase command interactively, execute git rebase -i with i option
  • select how much far back you want to rewrite commits, by passing the ordinal number of the commit to rebase onto
  • note: the rebase command rewrites every commit in the range HEAD~3..HEAD: do not include any commit already pushed to the central repository

Example: change the last three commit messages

# indicate 4 commits ago to change the last 3 commits
git rebase -i HEAD~3
  • note: the rebase command rewrites every commit in the range HEAD~3..HEAD: do not include any commit already pushed to the central repository
3 Example: rebasing the party-purchase-list

Create the party-purchase-list Git project and store the README file as first commit

user@host:~$ mkdir party-purchase-list
user@host:~$ cd party-purchase-list/
user@host:~/party-purchase-list$ git init
Initialized empty Git repository in /home/user/party-purchase-list/.git/
user@host:~/party-purchase-list$ nano README
user@host:~/party-purchase-list$ cat README 
# the purchase list for my 18th birthday party  
user@host:~/party-purchase-list$ git add .
user@host:~/party-purchase-list$ git commit -m "initial commit"
[master (root-commit) 57706cb] initial commit
 1 file changed, 1 insertion(+)
 create mode 100644 README

Add three more files: food.txt, drinks.txt and tableware.txt to the project

user@host:~/party-purchase-list$ nano food.txt
user@host:~/party-purchase-list$ git add .
user@host:~/party-purchase-list$ git commit food.txt -m "food to buy"
[master 5804e6f] food to buy
 1 file changed, 3 insertions(+)
 create mode 100644 food.txt
user@host:~/party-purchase-list$ nano drinks.txt
user@host:~/party-purchase-list$ git add .
user@host:~/party-purchase-list$ git commit drinks.txt -m "drinks to buy"
[master bd4b283] drinks to buy
 1 file changed, 3 insertions(+)
 create mode 100644 drinks.txt
user@host:~/party-purchase-list$ nano tableware.txt
user@host:~/party-purchase-list$ git add .
user@host:~/party-purchase-list$ git commit tableware.txt -m "tableware"
[master 7e0d393] tableware
 1 file changed, 5 insertions(+)
 create mode 100644 tableware.txt
user@host:~/party-purchase-list$ cat food.txt 
12 ham sanwiches
12 hamburger sandiwiches
12 salmon sandwiches
user@host:~/party-purchase-list$ cat drinks.txt 
3 bottles of coke
3 bottles of white wine
3 bottles of orange juice
user@host:~/party-purchase-list$ cat tableware.txt 
12 disposable forks
12 disposable knives
12 disposable spoons
24 plates and glasses
1 roll of paper towels
user@host:~/party-purchase-list$ 

Executing the gitk --all command, you will see that the master branch has four commits

Let's start the rebase tool

user@host:~/party-purchases-list$ git rebase -i HEAD~3

The rebase command opens an editor session:

Note that commits are listed in the opposite order than you would see them using the log command

user@host:~/party-purchase-list$ git log --oneline
7e0d393 (HEAD -> master) tableware
bd4b283 drinks to buy
5804e6f food to buy
57706cb initial commit

Suppose you want to edit the file food.txt of the second commit; modify the first line command from pick to edit

/home/user/party-purchase-list/.git/rebase-merge/git-rebase-todo   Modified  

edit 5804e6f food to buy
pick bd4b283 drinks to buy
pick 7e0d393 tableware

# Rebase 57706cb..7e0d393 onto 57706cb (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
                               [ Read 22 lines ]
^G Get Help  ^O Write Out ^W Where Is  ^K Cut Text  ^J Justify   ^C Cur Pos
^X Exit      ^R Read File ^\ Replace   ^U Uncut Text^T To Spell  ^_ Go To Line

After you save and exit, Git rewinds you back to the commit to be edited and drops you on the command line with the following message:

Stopped at 5804e6f...  food to buy
You can amend the commit now, with

  git commit --amend 

Once you are satisfied with your changes, run

  git rebase --continue

Load the file food.txt in the editor to add a new line to it

user@host:~/party-purchases-list$ nano food.txt
user@host:~/party-purchases-list$ cat food.txt 
12 ham sandwiches
12 hamburger sandwiches
12 salmon sandwiches
12 pizzas

If you run the git status command, you will notice that the food.txt file has been modified;

user@host:~/party-purchase-list$ git status
interactive rebase in progress; onto 57706cb
Last command done (1 command done):
   edit 5804e6f food to buy
Next commands to do (2 remaining commands):
   pick bd4b283 drinks to buy
   pick 7e0d393 tableware
  (use "git rebase --edit-todo" to view and edit)
You are currently editing a commit while rebasing branch 'master' on '57706cb'.
  (use "git commit --amend" to amend the current commit)
  (use "git rebase --continue" once you are satisfied with your changes)

Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working directory)

	modified:   food.txt

no changes added to commit (use "git add" and/or "git commit -a")

so, you have stage the modified file:

user@host:~/party-purchase-list$ git add food.txt 

now run the git commit --amend command to make the amendments to previous version of the food.txt file. This allows you to modify the commit message as well.

user@host:~/party-purchase-list$ git commit --amend
[detached HEAD 2fb20d1] food for my birthday party
 Date: Sat Jun 5 17:53:50 2021 +0200
 1 file changed, 4 insertions(+)
 create mode 100644 food.txt

After the git commit --amend command, you get a new branch, you can see this by executing the graphical interface: gitk --all or by executing the git branch -v command.

 user@host:~/party-purchases-list$ git branch -v
 * (no branch, rebasing master) 2fb20d1 food for my birthday party
   master                       7e0d393 tableware

Now, complete the rebasing and merge the branch

user@host:~/party-purchases-list$ git rebase --continue
Successfully rebased and updated refs/heads/master.

7 Configuration

Setting your name and email address

$ git config --global user.email "me@gmail.com"
$ git config --global user.name "Max Emon"
Basic Client Configuration

core editor

$ git config --global core.editor "nano -w"

Resource

Pro Git book

No comments:

Post a Comment