This tutorial will take a modular approach. The first section will provide an overview of the basic concepts of git. The second section will provide a quick overview of basic usage and the third and final section will cover intermediate level usage. In an attempt to ease understanding, the tutorial will blend together git commands and output, schematic diagrams and commentary in an attempt to ease understanding.
The following table surves as both a key and overview of the most common actions and git ‘verbs’.
git revert "<commit>" where <commit> is a reference to a commit that should be nullified (inverted)
Generate a new commit that reverses the changes introduced by a commit thereby effectively rolling back to a previous state (the one prior to the nominated commit) whilst still maintaining full commit history.
git pull -u <remote> <branch> where <remote> is the name of the remote (typically origin) and <branch> is the branch to sync with remote (typically master).
Pull changes from a branch of a remote repository.
git push -u <remote> <branch> where <remote> is the name of the remote (typically origin) and <branch> is the branch to sync with remote (typically master).
Push changes up to a branch of a remote repository.
1 Context
Git is a distributed versioning system. This means that the complete contents and history of a repository (in simplistic terms a repository is a collection of files and associated metadata) can be completely duplicated across multiple locations.
No doubt you have previously been working on a file (could be a document, spreadsheet, script or any other type of file) and got to a point where you have thought that you are starting to make edits that substantially change the file and therefore have considered saving the new file with a new name that indicates that it is a new version.
In the above diagram, new content is indicated in red and modifications in blue.
Whist this approach is ok, it is fairly limited and unsophisticated approach to versioning (keeping multiple versions of a file). Firstly, if you edit this file over many sessions and each time save with a different name, it becomes very difficult to either keep tract of what changes are associated with each version of the file, or the order in which the changes were made. This is massively compounded if a project comprises multiple files or has multiple authors.
Instead, imagine a system in which you could take a snapshot of state of your files and also provide a description outlining what changes you have made. Now imagine that the system was able to store and keep track of a succession of such versions in such a way that allows you to roll back to any previous versions of the files and exchange the entire history of changes with others collaborators - that is the purpose of git.
In the above diagram (which I must point out is not actually how git works), you can see that we are keeping track of multiple documents and potentially multiple changes within each document. What constitutes a version (as in how many changes and to what files) is completely arbitrary. Each individual edit can define a separate version.
One of the issues with the above system is that there is a lot of redundancy. With each new version an addition copy of the project’s entire filesystem (all its files) must be stored. In the above case, Version 2 and 3 both contain identical copies of fileA.doc. Is there a way of reducing the required size of the snapshots by only keeping copies of those that have actually changed? this is what git achieves. Git versions (or snapshots known as commits) store files that have changed since the previous and files that have not changed are only represented by links to instances of these files within previous snapshots.
Now consider the following:
You might have noticed that a new version can comprise multiple changes across multiple files. However, what if we have made numerous changes to numerous files over the course of an editing session (perhaps simultaneously addressing multiple different editing suggestions at a time), yet we did not want to lump all of these changes together into a single save point (snapshot). For example, the multiple changes might constitute addressing three independent issues, so although all edits were made simultaneously, we wish to record and describe the changes in three separate snapshots.
What if this project had multiple contributors some of whom are working on new components of the project and some whom are working simultaneously on the same set of files? How can the system ensure that all contributors are in sync with each other and that new components are only introduced to the project proper once they are stable and agreed upon?
What if there are files present within our project that we do not wish to keep track of. These files could be log files, compilation intermediates etc.
Given that projects can comprise many files (some of which can be large), is it possible to store compressed files so as to reduce the storage and bandwidth burden?
2 Overview of git
The above discussion provides context for understanding how git works. Within git, files can exist in one of four states:
untracked - these are files within the directory tree that are not to be included in the repository (not part of any snapshot)
modified - these are files that have changed since the last snapshot
staged - these are files that are nominated to be part of the next snapshot
committed - these are files that are represented in a stored snapshot (called a commit). One a snapshot is committed, it is a permanent part of the repositories history
Since untracked files are not part of a repository, we will ignore these for now.
Conceptually, there are three main sections of a repository:
Working directory - (or Workspace) is the obvious tree (set of files and folders) that is present on disc and comprises the actual files that you directly create, edit etc.
Staging area - (or index) is a hidden file that contains metadata about the files to be included in the next snapshot (commit)
Repository - the snapshots (commits). The commits are themselves just additional metadata pointing to a particular snapshot.
A superficial representation of some aspects of the git version control system follows. Here, the physical file tree in the workspace can be added to the staging area before this snapshot can be committed to the local repository.
After we add the two files (file 1 and file 2), both files will be considered in an untracked state. Adding the files to the staging area changes their state to staged. Finally when we commit, the files are in a committed state.
Now if we add another file (file 3) to our workspace, add this file to the staging area and then commit the change, the resulting committed snapshot in the local repository will resemble the workspace. Note, although the staging area contains all three files, only file 3 points to any new internal content - since file 1 and file 2 have unmodified, their instances in the staging area point to the same instances as previous. Similarly, the second commit in the Local repository will point to one new representation (associated with file 3) and two previous representations (associated with file 1 and file 2).
Initially, it might seem that there is an awful lot of duplication going on. For example, if we make a minor alteration to a file, why not just commit the change (delta) instead of an entirely new copy? Well, periodically, git will perform garbage collection on the repository. This process repacks the objects together into a single object that comprises only the original blobs and their subsequent deltas - thereby gaining efficiency. The process of garbage collection can also be forced at any time via:
git gc
During the evolution of most projects, situations arise in which we wish to start work on new components or features that might represent a substantial deviation from the main line of evolution. Often, we would very much like to be able to quarantine the main thread of the project from these new developments. For example, we may wish to be able to continue tweaking the main project files (in order to address minor issues and bugs), while at the same time, performing major edits that take the project in a different direction.
This is called branching. The main evolutionary thread of the project is referred to as the mainbranch. Deviations from the main branch are generally called branches and can be given any name (other than ‘main’ or ‘HEAD’). For example, we could start a new branch called ‘Feature’ where we can evolve the project in one direction whilst still being able to actively develop the main branch at the same time. ‘Feature’ and ‘main’ branches are depicted in the left hand sequence of circles of the schematic below.
The circles represent commits (stored snapshots). We can see that the first commit is the common ancestor of the ‘Feature’ and ‘main’ branch. HEAD is a special reference that points to the tip of the currently active commit. It indicates where the next commit will be built onto. In diagram above, HEAD is pointing to the last commit in main. Hence the next commit will build on this commit. To develop the Featurebranch further, we first have to move HEAD to the tip of the Featurebranch.
We can later merge the Featurebranch into the mainbranch in order to make the new changes mainstream.
To support collaboration, there can also be a remote repository (referred to as origin and depicted by the squares in the figure above). Unlike a local repository, a remote repository does not contain a workspace as files are not directly edited in the remote repository. Instead, the remote repository acts as a permanently available conduit between multiple contributors.
In the diagram above, we can see that the remote repository (origin) has an additional branch (in this called dev). The collaborator whose local repository is depicted above has either not yet obtained (pulled) this branch or has elected not to (as perhaps it is not a direction that they are involved in).
We also see that the mainbranch on the remote repository has a newer (additional) commit than the local repository.
Prior to working on branch a collaborator should first get any updates to the remote repository. This is a two step process. Firstly, the collaborator fetches any changes and then secondly merges those changes into their version of the branch. Collectively, these two actions are called a pull.
To make local changes available to others, the collaborator can pushcommits up to the remote repository. The pushed changes are applied directly to the nominated branch so it is the users responsibility to ensure as much as possible, their local repository already included the most recent remote repository changes (by always pulling before pushing).
To verify that the software is installed and accessible, open a terminal and issue the following:
git--version
git version 2.46.0
Unsure how to open a terminal?
Windows:
On Windows, you can access a terminal via one of the following:
via the command Prompt:
Press Win + R to open the Run dialog.
Type cmd and press Enter.
via PowerShell:
Press Win + X and select “Windows PowerShell.”
Git Bash (Optional):
if Git is installed (which we are hoping it is!), open “Git Bash” for a Unix-like terminal experience.
MacOS:
via Terminal:
Press Cmd + Space to open Spotlight.
Type terminal and press Enter.
Linux:
Oh please. You cannot seriously tell me that you are using Linux and don’t know how to access a terminal.
In the command above, pay particular attention to the number of hyphens in the above command - there are two in a row and no spaces between the -- and the word version.
If you get output similar to above (an indication of what version of git you have on your system), then it is likely to be properly installed. If instead you get an error message, then it is likely that git is not properly installed and you should try again.
4 Getting started
Before using git, it is a good idea to define some global (applied to all your gits) settings. These include your name and email address and whilst not essential, they are applied to all actions you perform so the it is easier for others to track the route of changes etc.
In the above, you should replace “Your Name” with your actual name. This need not be a username (or even a real name) it is not cross referenced anywhere. It is simply to use in collaboration so that your collaborators know who is responsible for your commits.
Similarly, you should replace “your_email@whatever.com” with an email that you are likely to monitor. This need not be the same email address you have used to register a Github account etc, it is just so that collaborators have a way of contacting you.
The remaining sections go through the major git versioning concepts. As previously indicated, git is a command driven program (technically a family of programs). Nevertheless, many other applications (such as RStudio) are able to interface directly with git for some of the more commonly used features. Hence, in addition to providing the command line syntax for performing each task, where possible, this tutorial will also provide instructions (with screen captures) for RStudio and emacs.
5 Setting up (initializing) a new repository
For the purpose of this tutorial, I will create a temporary folder the tmpfolder of my homedirectory into which to create and manipulate repositories. To follow along with this tutorial, you are encouraged to do similarly.
We will start by creating a new directory (folder) which we will call Repo1 in which to place our repository. All usual directory naming rules apply since it is just a regular directory.
mkdir ~/tmp/Repo1
To create (or initialize) a new local repository, issue the git initcommand in the root of the working directory you wish to contain the git repository. This can be either an empty directory or contain an existing directory/file structure. The git initcommand will add a folder called .git to the directory. This is a one time operation.
cd ~/tmp/Repo1git init
Initialized empty Git repository in /home/runner/tmp/Repo1/.git/
The .gitfolder contains all the necessary metadata to manage the repository.
this file stores settings such as the location of a remote repository that this repository is linked to.
description
lists the name (and version) of a repository
HEAD
lists a reference to the current checked out commit.
hooks
a directory containing scripts that are executed at various stages (e.g. pre-push.sample is an example of a script executed prior to pushing)
info
contains a file exclude that lists exclusions (files not to be tracked). This is like .gitignore, except is not versioned.
objects
this directory contains SHA indexed files being tracked
refs
a master copy of all the repository refs
logs
contains a history of each branch
The repository that we are going to create in this demonstration could be considered to be a new standalone analysis. In Rstudio, this would be considered a project. So, we will initialise the git repository while we create a new Rstudio project. To do so:
click on the Project selector in the top right of the Rstudio window (as highlighted by the red ellipse in the image below.
select New Project from the dropdown menu
select New Directory form the Create Project panel
select New Project from the Project Type panel
Provide a name for the new directory to be created and use the Browse button to locate a suitable position for this new directory. Ensure that the Create a git repository checkbox is checked
Click the Create Project button
If successful, you should notice a couple of changes - these are highlighted in the following figure:
a new Git tab will appear in the top right panel
the contents of this newly created project/repository will appear in the Files tab of the bottom right panel
If the files and directories that begin with a . do not appear, click on the More file commands cog and make sure the Show Hidden Files option is ticked.
The newly created files/folders are:
.git - this directory houses the repository information and should not generally be edited directly
.gitignore - this file defines files/folders to be excluded from the repository. We will discuss this file more later
.Rhistory - this file will accrue a history of the commands you have evaluated in R within this project
.Rproj.user - this folder stores some project-specific temporary files
Repo1.Rproj - contains the project specific settings
Note that on the left side of the Rstudio window there are two panels - one called “Console”, the other called “Terminal”. The console window is for issuing R commands and the terminal window is for issuing system (bash, shell) commands. Throughout this tutorial, as an alternative to using the point and click Rstudio methods, you could instead issue the Terminal instructions into the “Terminal” panel. Indeed, there are some git commands that are not supported directly by Rstudio and can only be entered into the terminal
Note, at this stage, no files are being tracked, that is, they are not part of the repository.
To assist in gaining a greater understanding of the workings of git, we will use a series of schematics diagrams representing the contents of four important sections of the repository. Typically, these figures will be contained within callout panels that expand/collapse upon clicking. However, for this first time, they will be standalone.
In the first figure below, the left hand panel represents the contents of the root directory (excluding the .git folder) - this is the workspace and is currently empty.
The three white panels represent three important parts of the inner structure of the .git folder. A newly initialized repository is relatively devoid of any specific metadata since there are no staged or committed files. In the root of the .git folder, there is a file called HEAD.
The figure is currently very sparse. However, as the repository grows, so the figure will become more complex.
The second figure provides the same information, yet via a network diagram. Again, this will not be overly meaningful until the repository contains some content.
5.2 Initializing other types of repositories
The above demonstrated how to initialise a new local repository from scratch. However, there are times when we instead want to:
create a git repository from an existing directory or project
collaborate with someone on an existing repository
create a remote repository
These situations are briefly demonstrated in the following sections.
5.2.1 Initializing a shared (remote) repository
The main repository for sharing should not contain the working directory as such - only the .git tree and the .gitignore file. Typically the point of a remote repository is to act as a perminantly available repository from which multiple uses can exchange files. Consequently, those accessing this repository should only be able to interact with the .git metadata - they do not directly modify any files.
Since a remote repository is devode of the working files and directories, it is referred to as bare.
To create a bare remote repository, issue the git init --barecommand after logging in to the remote location.
git init --bare
Use the instructions for the Terminal
5.2.2 Cloning an existing repository
To get your own local copy of an existing repository, issue the git clone <repo url>command in the root of the working directory you wish to contain the git repository. The repo url points to the location of the existing repository to be cloned. This is also a one time operation and should be issued in an otherwise empty directory.
The repo url can be located on any accessible filesytem (local or remote). The cloning process also stores a link back to the original location of the repository (called origin). This provides a convenient way for the system to keep track of where the local repository should exchange files.
Many git repositories are hosted on sites such as github, gitlab or bitbucket. Within an online git repository, these sites provide url links for cloning.
where "url.git" is the url of the hosted repository.
click on the Project selector in the top right of the Rstudio window (as highlighted by the red ellipse in the image below.
select New Project from the dropdown menu
select Version Control form the Create Project panel
select Git from the Create Project from Version Control panel
paste in the address of the repository that you want to clone, optionally a name for this repository (if you do not like the original name) and use the Browse button to locate a suitable position for this new directory.
Click the Create Project button
5.2.3 Initializing a repository in an existing directory
click on the Project selector in the top right of the Rstudio window (as highlighted by the red ellipse in the image below.
select New Project from the dropdown menu
select Existing Directory form the Create Project panel
use the Browse button to locate the existing directory
Click the Create Project button
6 Tracking files
The basic workflow for tracking files is a two step process in which one or more files are first added to the staging area before they are committed to the local repository. The staging area acts as a little like a snapshot of what the repository will look like once the changes have been committed. The staging area also acts like a buffer between the files in the workspace (actual local copy of files) and the local repository (committed changes).
The reason that this is a two step process is that it allows the user to make edits to numerous files, yet block the commits in smaller chunks to help isolate changes in case there is a need to roll back to previous versions.
6.1 Staging files
When a file is first added to the staging area, a full copy of that file is added to the staging area (not just the file diffs as in other versioning systems).
To demonstrate lets create a file (a simple text file containing the string saying ‘File 1’) and add it to the staging area.
echo'File 1'> file1
Now lets add this file to the staging area
git add file1
To see the status of the repository (that is, what files are being tracked), we issue the git statuscommand
git status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: file1
This indicates that there is a single file (file1) in the staging area
To demonstrate lets create a file (a simple text file containing the string saying ‘File 1’) and add it to the staging area.
Click the green “New File” button followed by the “Text File” option (or click the equivalent option from the “File” menu)
Type File 1 in the panel with the flashing cursor. This panel represents the contents of the yet to be named file that we are creating.
Click the “Save” or “Save all” buttons (or select the equivalent items from the “File” menu) and name the file “file1”
Switch to the Git tab and you should notice a number of items (including the file we just created) in the panel. These are files that git is aware of, but not yet tracking. This panel acts as a status window. The yellow “?” symbol indicates that git considers these files “untracked”
To stage a file, click on the corresponding checkbox - the status symbol should change to a green “A” (for added)
Our simple overview schematic represents the staging of file 1.
A schematic of the internal working of git shows in .git/objects a blob has been created. This is a compressed version of file1. Its filename is a 40 digit SHA-1 checksum has representing the contents of the file1. To re-iterate, the blob name is a SHA-1 hash of the file contents (actually, the first two digits form a folder and the remaining 38 form the filename).
We can look at the contents of this blob using the git cat-filecommand. This command outputs the contents of a compressed object (blob, tree, commit) from either the objects name (or unique fraction thereof) or its tag (we will discuss tags later).
git cat-file blob 50fcd
File 1
The add (staging) process also created a index file. This file simply points to the blob that is part of the snapshot. The git internals schematic illustrates the internal changes in response to staging a file.
To commit a set of changes from the staging area to the local repository, we issue the git commitcommand. We usually add the -mswitch to explicitly supply a message to be associated with the commit. This message should ideally describe what the changes the commit introduces to the repository.
We now see that the status has changed. It indicates that the tree in the workspace is in sync with the repository.
git status
On branch main
nothing to commit, working tree clean
To commit a set of changes from the staging area to the local repository:
click on the “Commit” button to open the “Review Changes” window
This box will list the files to be committed (in this case “file1”), the changes in this file since the previous commit (as this is the first time this file has been committed, the changes are the file contents)
you should also provide a commit message (in the figure above, I entered “Initial commit”. This message should ideally describe what the changes the commit introduces to the repository.
click the “Commit” button and you will be presented with a popup message.
This message provides feedback to confirm that your commit was successful.
close the popup window and the “Review Changes” window
file1 should now have disappeared from the git status panel.
Our simple overview schematic represents the staging of file 1.
Additional details about the commit
The following modifications have occurred (in reverse order to how they actually occur):
The mainbranch reference was created. There is currently only a single branch (more on branches later). The branch reference point to (indicates) which commit is the current commit within a branch.
cat .git/refs/heads/main
bab510ee0f7c10b81baaa46e41e145680bd308bc
A commit was created. This points to a tree (which itself points to the blob representing file1) as well as other important metadata (such as who made the commit and when). Since the time stamp will be unique each time a snapshot is commited, so too the name of the commit (as a SHA-1 checksum hash) will differ. To reiterate, the names of blobs and trees are determined by contents alone, commit names are also incorporate commit timestamp and details of the committer - and are thus virtually unique.
git cat-file commit bab51
tree 07a941b332d756f9a8acc9fdaf58aab5c7a43f64
author pcinereus <i.obesulus@gmail.com> 1726365453 +0000
committer pcinereus <i.obesulus@gmail.com> 1726365453 +0000
Initial repo and added file1
A tree object was created. This represents the directory tree of the snapshot (commit) and thus points to the blobs.
Whenever a file is added or modified, if the changes are to be tracked, the file needs to be added to the staging area. Lets demonstrate by modifying file1 and adding an additional file (file2), this time to a subdirectory (dir1).
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: dir1/file2
modified: file1
modify file1 by adding a number of hyphens under the File 1 like in the figure below
save the file. As you do so, you should notice that the file reappears in the status panel (this time with a blue “M” to signify that the file has been modified)
to create the subdirectory, click on the “Add a new folder” icon and then enter a name for the subdirectory in the popup box (as per figure below)
navigate to this new directory (dir1)
click the “Create a new blank file in current directory” button and select “Text file”
enter a new filename (file2) into the popup box
enter some text into this file (like in the figure below)
save the file and notice that the dir1 directory is now also in the git status panel (yet its status is “untracked”)
stage both file1 and dir1 (click on the corresponding checkboxes)
And now our schematic looks like:
Another visual representation of the git
More information about staging changes to the repository
So when staging, the following has been performed:
two new blobs have been generated. One representing the modified file1 and the other representing the newly created file2 in the dir1 folder. The blob that represented the original file1 contents is still present and indeed is still the one currently committed. Blobs are not erased or modified.
git commit -m'Modified file1 and added file2 (in dir1)'
[main 235d5df] Modified file1 and added file2 (in dir1)
2 files changed, 2 insertions(+)
create mode 100644 dir1/file2
click the “Commit” button
you might like to explore the changes associated with each file
enter a commit message (as in the figure below)
click the “Commit” button
after checking that the “Git Commit” popup does not contain any errors, close the popup
to explore the repository history, click towards the “History” button on the top left corner of the “Review Changes” window
This provides a graphical list of commits (in reverse chronological order)
once you have finished exploring the history, you can close the “Review Changes” window
More information about changes to the repository
The following modifications occur:
the master branch now points to the new commit.
cat .git/refs/heads/main
235d5dfe6ce2ac1c509d5aaded38cf0d1953cb29
git reflog
235d5df HEAD@{0}: commit: Modified file1 and added file2 (in dir1)
bab510e HEAD@{1}: commit (initial): Initial repo and added file1
a new commit was created. This points to a new root tree object and also points to the previous commit (its parent).
git cat-file commit 235d5
tree 2b61e2b3db9d1708269cf9d1aeaae2b0a2af1a23
parent bab510ee0f7c10b81baaa46e41e145680bd308bc
author pcinereus <i.obesulus@gmail.com> 1726365459 +0000
committer pcinereus <i.obesulus@gmail.com> 1726365459 +0000
Modified file1 and added file2 (in dir1)
new root tree was created. This points to a blob representing the modified file1 as well as a newly created sub-directory tree representing the dir1 folder.
git ls-tree 2b61e
040000 tree f2fa54609fe5e918f365e0d5ffaf9a3aea88d541 dir1
100644 blob 28ed2456cbfa8a18a280c8af5b422e91e88ff64d file1
git cat-file -p HEAD^{tree}
040000 tree f2fa54609fe5e918f365e0d5ffaf9a3aea88d541 dir1
100644 blob 28ed2456cbfa8a18a280c8af5b422e91e88ff64d file1
a new sub-directory root tree was created. This points to a blob representing the modified file1 as well as a newly created subtree tree representing the file2 file within the dir1 folder.
git ls-tree 235d5
040000 tree f2fa54609fe5e918f365e0d5ffaf9a3aea88d541 dir1
100644 blob 28ed2456cbfa8a18a280c8af5b422e91e88ff64d file1
OR,
git ls-tree HEAD
040000 tree f2fa54609fe5e918f365e0d5ffaf9a3aea88d541 dir1
100644 blob 28ed2456cbfa8a18a280c8af5b422e91e88ff64d file1
Another visual representation of the git
More information about committing changes to the repository
Committing staged changes creates an object under the .git tree.
tree 2b61e2b3db9d1708269cf9d1aeaae2b0a2af1a23
parent bab510ee0f7c10b81baaa46e41e145680bd308bc
author pcinereus <i.obesulus@gmail.com> 1726365459 +0000
committer pcinereus <i.obesulus@gmail.com> 1726365459 +0000
Modified file1 and added file2 (in dir1)
git cat-file -p HEAD^{tree}
040000 tree f2fa54609fe5e918f365e0d5ffaf9a3aea88d541 dir1
100644 blob 28ed2456cbfa8a18a280c8af5b422e91e88ff64d file1
git log --oneline
235d5df Modified file1 and added file2 (in dir1)
bab510e Initial repo and added file1
Now you might be wondering… What if I have modified many files and I want to stage them all. Do I really have to add each file individually? Is there not some way to add multiple files at a time? The answer of course is yes. To stage all files (including those in subdirectories) we issue the git add .command (notice the dot).
git add .
6.4.gitignore
Whilst it is convenient to not have to list every file that you want to be staged (added), what about files that we don’t want to get staged and committed. It is also possible to define a file (called .gitignore) that is a list of files (or file patterns) that are to be excluded when we request all files be added. This functionality is provided via the .gitignorefile that must be in the root of the repository working directory.
For example, we may have temporary files or automatic backup files or files generated as intermediates in a compile process etc that get generated. These files are commonly generated in the process of working with files in a project, yet we do not necessarily wish for them to be tracked. Often these files have very predictable filename pattern (such as ending with a # or ~ symbol or having a specific file extension such as .aux.
As an example, when working with a project in Rstudio, files (such as .Rhistory) and directories (such as .Rproj.user) are automatically added to the file system and thus appear as untracked files in git status.
Hence, we can create a.gitignore to exclude these files/directories. Indeed, if you are using Rstudio, you might have noticed that a .gitignore file was automatically created when you created the project.
Lets start by modifying the file2 and creating a new file f.tmp (that we want to ignore).
navigate to the dir1 directory and open file2 for editing (or just make sure you are on the file2 tab.
edit the file such that it just contains three hyphens (---) before saving the file
in the same dir1 directory add another new text file (f.tmp) and edit this file to contain the word temp (then save the file)
The Git status panel should display both of these as untracked files.
To ignore the f.tmp file, we could either explicitly add this file as a row in a .gitignore file, or else we could supply a wildcard version that will ignore all files ending in .tmp.
click on the gitignore file to open it up for editing
navigate to the end of this file and add a newline containing the text *.tmp
You will notice that this .gitignore file already had items in it before you started editing it. These were added by Rstudio when you first created the new project.
The first item is .Rproj.user and its presence in this file is why it does not appear in the git status panel.
Once we save the .gitignore file, notice how the f.tmp file is similarly removed from the git status panel - since via .gitignore we have indicated that we want to ignore this file (not track it as part of our version control system).
More information about exclusions (.gitignore)
Entry
Meaning
file1
DO NOT stage (add) file1
*.tmp
DO NOT stage (add) any file ending in .tmp
/dir1/*
DO NOT stage (add) the folder called dir1 (or any of its contents) unless this is specifically negated (see next line)
!/dir1/file2
DO stage (add) the file called file2 in the dir1 folder
Now when we go to add all files to the staging area, those that fall under the exclude rules will be ignored
d540207 Modified file2, added .gitignore
235d5df Modified file1 and added file2 (in dir1)
bab510e Initial repo and added file1
7 Inspecting a repository
For this section, will will be working on the repository built up in the previous section. If you did not follow along with the previous section, I suggest that you expand the following callout and run the provided code in a terminal.
If you already have the repository, you can ignore the commands to create the repository.
Recall that within the .git environment, files can be in one of four states:
untracked
modified
staged
committed
To inspect the status of files in your workspace, you can issue the git statuscommand (as we have done on numerous occasions above). This command displays the current state of the workspace and staging area.
On branch main
nothing to commit, working tree clean
The output of git status partitions all the files into (staged: Changes to be committed, unstaged: Changes not staged for commit and Untracked) as well as hints on how to either promote or demote the status of these files.
Examine the git status panel - ideally it should be empty thereby signalling that all your important files are tracked andcommitted.
The git logcommand allows us to review the history of committed snapshots
git log
commit d5402077556abfd38a986b131a44b42c10fd29ba
Author: pcinereus <i.obesulus@gmail.com>
Date: Sun Sep 15 01:57:41 2024 +0000
Modified file2, added .gitignore
commit 235d5dfe6ce2ac1c509d5aaded38cf0d1953cb29
Author: pcinereus <i.obesulus@gmail.com>
Date: Sun Sep 15 01:57:39 2024 +0000
Modified file1 and added file2 (in dir1)
commit bab510ee0f7c10b81baaa46e41e145680bd308bc
Author: pcinereus <i.obesulus@gmail.com>
Date: Sun Sep 15 01:57:33 2024 +0000
Initial repo and added file1
We can see that in my case some fool called ‘Murray Logan’ has made a total of three commits. We can also see the date/time that the commits were made as well as the supplied commit comment.
Over time repositories accumulate a large number of commits, to only review the last 2 commits, we could issue the git log -n 2command.
git log -n 2
commit d5402077556abfd38a986b131a44b42c10fd29ba
Author: pcinereus <i.obesulus@gmail.com>
Date: Sun Sep 15 01:57:41 2024 +0000
Modified file2, added .gitignore
commit 235d5dfe6ce2ac1c509d5aaded38cf0d1953cb29
Author: pcinereus <i.obesulus@gmail.com>
Date: Sun Sep 15 01:57:39 2024 +0000
Modified file1 and added file2 (in dir1)
Additional useful (git log) options
Option
Example
--oneline Condensed view
git log --oneline
d540207 Modified file2, added .gitignore
235d5df Modified file1 and added file2 (in dir1)
bab510e Initial repo and added file1
--grep="<pattern>" Filter by regex pattern of commit message
git log --grep="Modified"
commit d5402077556abfd38a986b131a44b42c10fd29ba
Author: pcinereus <i.obesulus@gmail.com>
Date: Sun Sep 15 01:57:41 2024 +0000
Modified file2, added .gitignore
commit 235d5dfe6ce2ac1c509d5aaded38cf0d1953cb29
Author: pcinereus <i.obesulus@gmail.com>
Date: Sun Sep 15 01:57:39 2024 +0000
Modified file1 and added file2 (in dir1)
<file> Filter by filename
git log file1
commit 235d5dfe6ce2ac1c509d5aaded38cf0d1953cb29
Author: pcinereus <i.obesulus@gmail.com>
Date: Sun Sep 15 01:57:39 2024 +0000
Modified file1 and added file2 (in dir1)
commit bab510ee0f7c10b81baaa46e41e145680bd308bc
Author: pcinereus <i.obesulus@gmail.com>
Date: Sun Sep 15 01:57:33 2024 +0000
Initial repo and added file1
--decorate --graph
git log --graph--decorate--oneline
* d540207 (HEAD -> main) Modified file2, added .gitignore
* 235d5df Modified file1 and added file2 (in dir1)
* bab510e Initial repo and added file1
--all All branches
git log --all
commit d5402077556abfd38a986b131a44b42c10fd29ba
Author: pcinereus <i.obesulus@gmail.com>
Date: Sun Sep 15 01:57:41 2024 +0000
Modified file2, added .gitignore
commit 235d5dfe6ce2ac1c509d5aaded38cf0d1953cb29
Author: pcinereus <i.obesulus@gmail.com>
Date: Sun Sep 15 01:57:39 2024 +0000
Modified file1 and added file2 (in dir1)
commit bab510ee0f7c10b81baaa46e41e145680bd308bc
Author: pcinereus <i.obesulus@gmail.com>
Date: Sun Sep 15 01:57:33 2024 +0000
Initial repo and added file1
To explore the history of a repository, click on the clock icon (“View history of previous commits” button). This will open up the “Review Changes” window in the “History” tab.
Along with the reverse chronological list of commits, for each commit (and file thereof), you can explore the changes (diffs) that occurred.
Text that appears over a green background represents text that have been added as part of the current commit. Text that appears over a red background represents text that have been removed.
If we scroll down and explore the changes in dir1/file2 for the most recent commit, we see that the text * Notes was removed and then * Notes and --- were added. At first this might seem a bit odd - why was * Notes deleted and then added back?
Git works on entire lines of text. So the first line was replaced because in the newer version, the first line had a carriage return (newline character). Although we cant see this character, it is there - we see it more via its effect (sending the text after it to the next line). Hence, in fact, two lines of text were actually changed in the most recent commit.
Another way to explore the commit history is to look at the reflog. This is a log of the branch references. This approach is more useful when we have multiple branches and so will be visited in the section on branching. It displays all repository activity, not just the commits.
git reflog
d540207 HEAD@{0}: commit: Modified file2, added .gitignore
235d5df HEAD@{1}: commit: Modified file1 and added file2 (in dir1)
bab510e HEAD@{2}: commit (initial): Initial repo and added file1
Some of this sort of information can be gleaned from the git “History”. Just make sure you select (“all branches”) from the “Switch branch” menu.
7.1.3diff
Whilst some of these actions described in this section are available from the “History” tab of the “Review Changes” window in Rstudio, most are only available as terminal commands.
Two of the three commits in our repository involved modifications to a file (dir1/file2). To further help illustrate commands to compare files indifferent states, we will additionally make a further change to dir1/file2. The git diff allows us to explore differences between:
The output indicates that we are comparing the blob representing dir1/file2 in the index (staging area) with the newly modified dir1/file2. The next couple of rows indicate that the indexed version will be represented by a ‘-’ sign and the new version will be represented by a ‘+’ sign. The next row (which is surrounded in a pair of @ signs, indicates that there are two lines that have changed. Finally the next two rows show that a charrage return has been added to the end of the first line and the new version has added the word ‘Notes’ to the next line.
Similar to the previous section, the following is only really available via the terminal.
We can list the files that comprise the repository by:
git ls-files
.gitignore
dir1/file2
file1
The change to dir1/file2 above was just to illustrate the git diff. In doing so we now have a modified version of this file that has not been committed Before we move on, I am going to remove these changes so that the dir1/file2 is not in a modified state and reflects the state of the current commit. To do so, I will use perform a hard reset (git reset --hard). More will be discussed about the git reset command later in this tutorial - for now all that is important is to know that it restores the workspace to a previous state.
In addition to the git reset --hard, I will also clean and prune the repository.
HEAD is now at d540207 Modified file2, added .gitignore
7.2 Tags
Although it is possible to track the history of a repository via its commit sha1 names, most find it more convenient to apply tags to certain milestone commits. For example, a particular commit might represent a specific point in the history of a project - such as a release version. Git tags allow us to apply more human readable flags.
In the above, V.1 is the tag we are applying to the most recent commit. For this example, V.1 simply denotes something like “version 1”. The tag must not contain any spaces (just replace space characters with underscores or periods).
git log --graph--decorate--oneline
* d540207 (HEAD -> main, tag: V.1) Modified file2, added .gitignore
* 235d5df Modified file1 and added file2 (in dir1)
* bab510e Initial repo and added file1
git reflog
d540207 HEAD@{0}: reset: moving to HEAD
d540207 HEAD@{1}: commit: Modified file2, added .gitignore
235d5df HEAD@{2}: commit: Modified file1 and added file2 (in dir1)
bab510e HEAD@{3}: commit (initial): Initial repo and added file1
The functionality to add tags to commits is not directly supported in Rstudio. in order to apply a tag, you will need to switch the terminal and enter a command like:
git tag V.1
In the above, V.1 is the tag we are applying to the most recent commit. For this example, V.1 simply denotes something like “version 1”. The tag must not contain any spaces (just replace space characters with underscores or periods).
Now if we return to the “History” tab of the “Review Changes” window, we can see the tag represented by a yellow tag in the commit diagram.
Another visual representation of the repository
8 Branching
Again we will start with our repository. For this section, will be working on the repository built up in the previous section.
Lets assume that the current commit represents a largely stable project. We are about to embark on a substantial modification in the form of a new feature that will involve editing file1 and adding a new file to dir1. At the same time, we wish to leave open the possibility of committing additional minor changes to the current commit in order to address any bugs or issues that might arise.
In essence what we want to do is start a new branch for the new feature. This is performed in two steps:
use the git branch <name>command to generate a new branch reference. In the following example, I will call the new branch Feature, but it can be anything.
To create a new branch, click on the “New branch” icon (see figure below) and enter a name (e.g. Feature) for the branch in the popup box.
Note
Note, at this stage we have only created a reference to a new branch, until we commit to this branch, its contents will be the same as the main branch.
More information following the creation of the branch
git log --graph --decorate --oneline
* 3534d11 (HEAD -> main, tag: V.1, Feature) Modified file2, added .gitignore
* 8c416f9 Modified file1 and added file2 (in dir1)
* cf111fb Initial repo and added file1
To checkout a branch in Rstudio, click on the “Switch branch” button and select the branch name (in this case Feature) from the dialog box.
If we now examine the “History” tab of the “Review Changes” window, we will see that the most recent commit has both main and Feature branch markers. However, the main connection is with the Feature branch indicating that the HEAD is currently on the Feature branch.
The only noticable change is that we are now considered to be on the “Feature” branch (notice that HEAD is pointing to Feature). Any new commits will be applied to this new branch.
More information following the creation of the branch
git log --graph --decorate --oneline
* 3534d11 (HEAD -> Feature, tag: V.1, main) Modified file2, added .gitignore
* 8c416f9 Modified file1 and added file2 (in dir1)
* cf111fb Initial repo and added file1
tree -ra -L 2 --charset asciigit reflog
.
|-- file1
|-- dir1
| |-- file2
| `-- f.tmp
|-- .gitignore
`-- .git
|-- refs
|-- objects
|-- logs
|-- info
|-- index
|-- hooks
|-- description
|-- config
|-- branches
|-- HEAD
`-- COMMIT_EDITMSG
8 directories, 9 files
3534d11 HEAD@{0}: checkout: moving from main to Feature
3534d11 HEAD@{1}: commit: Modified file2, added .gitignore
8c416f9 HEAD@{2}: commit: Modified file1 and added file2 (in dir1)
cf111fb HEAD@{3}: commit (initial): Initial repo and added file1
Another visual representation of the repository
Now if we make and commit a change (such as an edit to file1 and an addition of file3 within dir1), we will be operating on a separate branch.
modify file1 by adding a carriage return followed by some additional text (e.g. a b) like in the figure below
navigate to this new directory (dir1)
click the “Create a new blank file in current directory” button and select “Text file”
enter a new filename (file3) into the popup box
enter some text into this file (such as File 3)
stage (add) the two modified files using their respective checkboxes
click the “Commit” button, provide a commit message (sch as “New feature”)
close the popup
goto the “History” tab
Notice that we have advanced one commit on this new branch.
More information following the creation of the branch
git log --graph --decorate --oneline
* 91826ac (HEAD -> Feature) New feature
* 3534d11 (tag: V.1, main) Modified file2, added .gitignore
* 8c416f9 Modified file1 and added file2 (in dir1)
* cf111fb Initial repo and added file1
tree -ra -L 2 --charset asciigit reflog
.
|-- file1
|-- dir1
| |-- file3
| |-- file2
| `-- f.tmp
|-- .gitignore
`-- .git
|-- refs
|-- objects
|-- logs
|-- info
|-- index
|-- hooks
|-- description
|-- config
|-- branches
|-- HEAD
`-- COMMIT_EDITMSG
8 directories, 10 files
91826ac HEAD@{0}: commit: New feature
3534d11 HEAD@{1}: checkout: moving from main to Feature
3534d11 HEAD@{2}: commit: Modified file2, added .gitignore
8c416f9 HEAD@{3}: commit: Modified file1 and added file2 (in dir1)
cf111fb HEAD@{4}: commit (initial): Initial repo and added file1
Another visual representation of the repository
So we can now continue to develop the Featurebranch. But what if we now decided that we wanted to make a change to the mainbranch (perhaps addressing a bug or issue).
3534d11 HEAD@{0}: checkout: moving from Feature to main
91826ac HEAD@{1}: commit: New feature
3534d11 HEAD@{2}: checkout: moving from main to Feature
3534d11 HEAD@{3}: commit: Modified file2, added .gitignore
8c416f9 HEAD@{4}: commit: Modified file1 and added file2 (in dir1)
cf111fb HEAD@{5}: commit (initial): Initial repo and added file1
Another visual representation of the repository
make the necessary changes to the files and commit them on the mainbranch
More information following the creation of the branch
git log --graph --decorate --oneline --all
* 8c73b66 (HEAD -> main) Bug fix in file1
| * 91826ac (Feature) New feature
|/
* 3534d11 (tag: V.1) Modified file2, added .gitignore
* 8c416f9 Modified file1 and added file2 (in dir1)
* cf111fb Initial repo and added file1
tree -ra -L 2 --charset asciigit reflog
.
|-- file1
|-- dir1
| |-- file2
| `-- f.tmp
|-- .gitignore
`-- .git
|-- refs
|-- objects
|-- logs
|-- info
|-- index
|-- hooks
|-- description
|-- config
|-- branches
|-- HEAD
`-- COMMIT_EDITMSG
8 directories, 9 files
8c73b66 HEAD@{0}: commit: Bug fix in file1
3534d11 HEAD@{1}: checkout: moving from Feature to main
91826ac HEAD@{2}: commit: New feature
3534d11 HEAD@{3}: checkout: moving from main to Feature
3534d11 HEAD@{4}: commit: Modified file2, added .gitignore
8c416f9 HEAD@{5}: commit: Modified file1 and added file2 (in dir1)
cf111fb HEAD@{6}: commit (initial): Initial repo and added file1
Another visual representation of the repository
We could simultaneously make additional modifications to the Featurebranch just by simply checking out the Featurebranch and commiting those modifications. To illustrate, we will make another change to the dir1/file3 file.
For this demonstration we are deliberately avoiding making edits to either file1 or dir1/file2. This is because if we did, there is a chance that we might introduce conflicting edits of the same lines of files across the two branches (main and Feature).
In a later section, we WILL deliberately introduce a conflict so that we can see how to resolve conflicts.
Finally, (if we are satisfied that Feature is stable and complete), we might like to introduce these changes into the mainbranch so that they become a part of the main project base. This operation is called a merge and is completed with the git merge <branch>command where <branch> is the name of the branch you want to merge the current branch (that pointed to by HEAD) with. Typically we want to merge the non-main branch with the mainbranch. Therefore we must be checkout the mainbranch before merging.
.
|-- file1
|-- dir1
| |-- file3
| |-- file2
| `-- f.tmp
|-- .gitignore
`-- .git
|-- refs
|-- objects
|-- logs
|-- info
|-- index
|-- hooks
|-- description
|-- config
|-- branches
|-- ORIG_HEAD
|-- HEAD
`-- COMMIT_EDITMSG
8 directories, 11 files
cbc5949 HEAD@{0}: merge Feature: Merge made by the 'ort' strategy.
8c73b66 HEAD@{1}: checkout: moving from Feature to main
aa38a67 HEAD@{2}: commit: Feature complete
91826ac HEAD@{3}: checkout: moving from main to Feature
8c73b66 HEAD@{4}: commit: Bug fix in file1
3534d11 HEAD@{5}: checkout: moving from Feature to main
91826ac HEAD@{6}: commit: New feature
3534d11 HEAD@{7}: checkout: moving from main to Feature
3534d11 HEAD@{8}: commit: Modified file2, added .gitignore
8c416f9 HEAD@{9}: commit: Modified file1 and added file2 (in dir1)
cf111fb HEAD@{10}: commit (initial): Initial repo and added file1
Another visual representation of the repository
Warning
If, when issuing a git merge command, you get a conflict message, please refer to the section on resolving conflicts below.
8.2 Delete a branch
Once the purpose of the branch has been fulfilled (for example to develop a new feature) and the branch has been merged back into the main branch, you might consider deleting the branch so as to simplify the commit history.
Importantly, this action should only ever be performed after the branch has been successfully merged into the main branch (in fact `git will not allow you to delete an un-merged branch unless you really fight for it).
Note also, this can only be performed on a local repository.
This procedure is only available via the terminal.
git branch -d Feature
Deleted branch Feature (was aa38a67).
More information following the creation of the branch
.
|-- file1
|-- dir1
| |-- file3
| |-- file2
| `-- f.tmp
|-- .gitignore
`-- .git
|-- refs
|-- objects
|-- logs
|-- info
|-- index
|-- hooks
|-- description
|-- config
|-- branches
|-- ORIG_HEAD
|-- HEAD
`-- COMMIT_EDITMSG
8 directories, 11 files
cbc5949 HEAD@{0}: merge Feature: Merge made by the 'ort' strategy.
8c73b66 HEAD@{1}: checkout: moving from Feature to main
aa38a67 HEAD@{2}: commit: Feature complete
91826ac HEAD@{3}: checkout: moving from main to Feature
8c73b66 HEAD@{4}: commit: Bug fix in file1
3534d11 HEAD@{5}: checkout: moving from Feature to main
91826ac HEAD@{6}: commit: New feature
3534d11 HEAD@{7}: checkout: moving from main to Feature
3534d11 HEAD@{8}: commit: Modified file2, added .gitignore
8c416f9 HEAD@{9}: commit: Modified file1 and added file2 (in dir1)
cf111fb HEAD@{10}: commit (initial): Initial repo and added file1
Another visual representation of the repository
In order to facilitate the rest of the tutorial, I am going to reset the repository to a commit that precedes the merge. The process of resetting will be covered later on in this tutorial.
.
|-- file1
|-- dir1
| `-- file2
|-- .gitignore
`-- .git
|-- refs
|-- packed-refs
|-- objects
|-- logs
|-- info
|-- index
|-- hooks
|-- description
|-- config
|-- branches
|-- ORIG_HEAD
|-- HEAD
`-- COMMIT_EDITMSG
8 directories, 10 files
* aa38a67 (Feature) Feature complete
* 91826ac New feature
| * 8c73b66 (HEAD -> main) Bug fix in file1
|/
* 3534d11 (tag: V.1) Modified file2, added .gitignore
* 8c416f9 Modified file1 and added file2 (in dir1)
* cf111fb Initial repo and added file1
8c73b66 HEAD@{0}: checkout: moving from Feature to main
aa38a67 HEAD@{1}: commit: Feature complete
91826ac HEAD@{2}: checkout: moving from main to Feature
8c73b66 HEAD@{3}: commit: Bug fix in file1
3534d11 HEAD@{4}: checkout: moving from Feature to main
91826ac HEAD@{5}: commit: New feature
3534d11 HEAD@{6}: checkout: moving from main to Feature
3534d11 HEAD@{7}: commit: Modified file2, added .gitignore
8c416f9 HEAD@{8}: commit: Modified file1 and added file2 (in dir1)
cf111fb HEAD@{9}: commit (initial): Initial repo and added file1
8.3 Rebasing
Branches are great for working on new features without disturbing a stable codebase. However, the main branch may have changed or progressed since the branch started. As a result, it may be building upon old code that may either no longer work or no longer be appropriate.
Whilst you could attempt to manually apply the newer main code changes into your branch, this is likely to be tedious and error prone. Rebasing is the process of changing the root (base) of the branch from one commit to another. In this way, the base of the branch can be moved to the current HEAD of the main branch, thereby absorbing all the updates from the main branch into the feature branch.
This section builds on the repository created up to this point in the tutorial. To remind you, the repository currently looks like:
* c745fdb (HEAD -> Feature) Feature complete
* 571359f New feature
| * c345300 (main) Bug fix in file1
|/
* 2c47c61 (tag: V.1) Modified file2, added .gitignore
* 0261f74 Modified file1 and added file2 (in dir1)
* 7bdfcce Initial repo and added file1
git reflog
c745fdb HEAD@{0}: commit: Feature complete
571359f HEAD@{1}: checkout: moving from main to Feature
c345300 HEAD@{2}: commit: Bug fix in file1
2c47c61 HEAD@{3}: checkout: moving from Feature to main
571359f HEAD@{4}: commit: New feature
2c47c61 HEAD@{5}: checkout: moving from main to Feature
2c47c61 HEAD@{6}: commit: Modified file2, added .gitignore
0261f74 HEAD@{7}: commit: Modified file1 and added file2 (in dir1)
7bdfcce HEAD@{8}: commit (initial): Initial repo and added file1
Rebasing (1/2)
Rebasing (2/2)
Successfully rebased and updated refs/heads/Feature.
Rebasing is not directly supported by Rstudio.
checkout the Feature branch using the “Switch branch” selector
in the terminal, type git rebase main to rebase the Feature branch on the end of the main branch.
if you now review the “History” tab of the “Review Changes” window, you will now see that the history is linear and the Feature branch stems from the end of the main branch. That is, we have moved the base of the Feature branch.
* 9ce032d (HEAD -> Feature) Feature complete
* b77c352 New feature
* c345300 (main) Bug fix in file1
* 2c47c61 (tag: V.1) Modified file2, added .gitignore
* 0261f74 Modified file1 and added file2 (in dir1)
* 7bdfcce Initial repo and added file1
git reflog
9ce032d HEAD@{0}: rebase (finish): returning to refs/heads/Feature
9ce032d HEAD@{1}: rebase (pick): Feature complete
b77c352 HEAD@{2}: rebase (pick): New feature
c345300 HEAD@{3}: rebase (start): checkout main
c745fdb HEAD@{4}: checkout: moving from Feature to Feature
c745fdb HEAD@{5}: commit: Feature complete
571359f HEAD@{6}: checkout: moving from main to Feature
c345300 HEAD@{7}: commit: Bug fix in file1
2c47c61 HEAD@{8}: checkout: moving from Feature to main
571359f HEAD@{9}: commit: New feature
2c47c61 HEAD@{10}: checkout: moving from main to Feature
2c47c61 HEAD@{11}: commit: Modified file2, added .gitignore
0261f74 HEAD@{12}: commit: Modified file1 and added file2 (in dir1)
7bdfcce HEAD@{13}: commit (initial): Initial repo and added file1
If the rebased commits were previously on a remote repository (hopefully you checked to make sure noone was relying on any of the commits that have been squashed), then it will be necessary to force a push on this repository.
9 Undoing (rolling back) changes
One of the real strengths of a versioning system is the ability to roll back to a previous state when changes have been found to introduce undesirable or unintended consequences. There are also multiple different stages from which to roll back. For example, do we want to revert from committed states or just unstage a file or files.
To illustrate the various ways to roll back within a repository, we will start with a small repository comprising of a single branch and just three commits. This repository will mimic a repository created earlier in this tutorial (before we started branching).
* b284e9b (HEAD -> main, tag: V.1) Modified file2, added .gitignore
* 6898aca Modified file1 and added file2 (in dir1)
* 1401d56 Initial repo and added file1
b284e9b HEAD@{0}: commit: Modified file2, added .gitignore
6898aca HEAD@{1}: commit: Modified file1 and added file2 (in dir1)
1401d56 HEAD@{2}: commit (initial): Initial repo and added file1
The above diagram shows that both HEAD and main point at the same stage (all three files). Again, remember that the SHA-1 has values will be different in your repo so in the following, you will need to use the SHA value that corresponds to the item in your list.
With additional commits and activity, the above schematic will rapidly become very busy and complex. As a result, we will now switch to a simpler schematic that focuses only on the commits and references thereof (HEAD, main and branches).
Recall that a git repository comprises multiple levels in which changes are recorded:
there is the Workspace (which is essentially the actual files and folders that you directly edit).
there is the Staging area (or index which is a record of which files are next to be committed).
there is the Local repository (the actual commits).
and finally, three is the remote repository (a remote store of commits).
As such, there are multiple levels from which changes could be undone. Furthermore, we might want to undo changes at the commit or individual file level. For example, we might decide that we have made a local commit that introduced an issue and we now wish to return back to the state prior to this commit. Alternatively, we might have just accidentally staged a file (yet not committed it) and now we want to unstage it.
Action
Command
Notes
Commit level
Undo to a particular local commit
git reset --soft <commit>
HEAD is moved to the nominated . IT DOES NOT alter index or the workspace
Roll back to the the previous commit
git reset --hard <commit>
Resets the Index and Workspace
Roll back over the last two commits
git reset --hard HEAD~2
Roll back over the last two commits
Inspect an old commit
git checkout <commit>
moves the HEAD and modifies the workspace to reflect its state at
Roll back the changes introduced by commit so that a new commit resembles a previous state
git revert HEAD
Creates a new commit that reverses the changes introduced by the last commit. Revert creates a new revision history that adds onto existing history and is therefore safe to use on a branch that has been pushed to a remote.
Now lets say we wanted to roll back to the state before we added .gitignore and modified dir1/file2. That is, we want to roll-back to commit 6898aca. We have three main choices:
reset - this allows us to remove all commits back to a nominated commit. Resetting is a irreversible process as it totally removes commits from the history. A reset should only ever be used if you are sure you want to permanently remove the changes introduced via one or more commits. A reset should never be performed on a branch that exists in a remote repository
revert - this allows us to skip the most recent commit. That is, a revert rolls back to a previous commit and then apply that state to a new commit. Unlike a reset, all commits remain safely in the git history and can target a single commit.
branch - this allows us to safely take the project (or part of the project) in an experimental direction that might involve dramatic deviations in files without interrupting the main thread of the project. At some point, if the new direction proves useful, the changes can be merged back into the main branch. We will expore branching in the section on branching.
Normally we would not perform all three. Rather, we would select the most appropriate one depending on the context and goal. Nevertheless, this is a tutorial and therefore we will perform all three. In order to ensure that we start from the same point for each demonstration, prior to each demonstration, we will aggressively reset the repository back to the state it was at commit 6898aca.
9.1 Reset
Reset is not directly supported by Rstudio - use the terminal for this section.
9.1.1 Soft reset
When we perform a soft reset, we move the head to the nominated commit, but the workspace is unchanged.
* b284e9b (tag: V.1) Modified file2, added .gitignore
* 6898aca (HEAD -> main) Modified file1 and added file2 (in dir1)
* 1401d56 Initial repo and added file1
6898aca HEAD@{0}: reset: moving to 6898aca
b284e9b HEAD@{1}: commit: Modified file2, added .gitignore
6898aca HEAD@{2}: commit: Modified file1 and added file2 (in dir1)
1401d56 HEAD@{3}: commit (initial): Initial repo and added file1
9.1.2 Hard reset
When we perform a hard reset, we not only move the head to the nominated commit, but the workspace is altered to reflect the workspace that existed when that commit was originally performed.
As I am about to demonstrate this on a repo that I have just performed a soft reset on, I am first going to start by re-establishing the original repository. If you have not just run a soft reset, then ignore the following.
* b284e9b (tag: V.1) Modified file2, added .gitignore
* 6898aca (HEAD -> main) Modified file1 and added file2 (in dir1)
* 1401d56 Initial repo and added file1
git reflog
6898aca HEAD@{0}: reset: moving to 6898aca
b284e9b HEAD@{1}: reset: moving to V.1
6898aca HEAD@{2}: reset: moving to 6898aca
b284e9b HEAD@{3}: commit: Modified file2, added .gitignore
6898aca HEAD@{4}: commit: Modified file1 and added file2 (in dir1)
1401d56 HEAD@{5}: commit (initial): Initial repo and added file1
Note, however, if we looked at the log, it would be as if the previous commit had not occurred. For this reason, care must be exercised when using reset on remote repositories since others may be relying on a specific point in the repo history that you may have just erased.
Another visual representation of the repository
If we now make a change (such as a change to file1 and adding file3) and commit, it would be as if any commits after 6898aca had never occurred.
As with git reset, git revert is not directly supported by Rstudio, hence the methods used in this section should be performed in the terminal. There is one exception to this, Rstudio is able to revert an modified file back to its state in the last commit.
HEAD is now at b284e9b Modified file2, added .gitignore
Revert generates a new commit that removes the changes that were introduced by one or more of the most recent commits. Note, it does not revert to a particular commit, but rather undoes a commit. So, to roll back to 6898aca (the second last commit), we just have to revert the last commit (HEAD).
Notice the absence of .gitignore. Notice also that dir1/f.tmp is also present. Although this file was added at the same time as .gitignore, it was never committed and therefore is not altered with repo manipulations.
If we list the files that are part of the repo:
git ls-files
dir1/file2
file1
we will see that we are back to the state where only file1 and dir1/file2 are present.
HEAD is now at b284e9b Modified file2, added .gitignore
If we wanted to review the state of files corresponding to commit 6898aca, we could checkout the code from that commit. This provides a way to travel back in time through your commits and explore the (tracked) files exactly as they were.
git checkout 6898a
Note: switching to '6898a'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 6898aca Modified file1 and added file2 (in dir1)
More information about this repository
git reflog
6898aca HEAD@{0}: checkout: moving from main to 6898a
6898aca HEAD@{1}: reset: moving to 6898aca
b284e9b HEAD@{2}: reset: moving to V.1
6898aca HEAD@{3}: reset: moving to 6898aca
b284e9b HEAD@{4}: commit: Modified file2, added .gitignore
6898aca HEAD@{5}: commit: Modified file1 and added file2 (in dir1)
1401d56 HEAD@{6}: commit (initial): Initial repo and added file1
git log --graph --decorate --oneline --all
* b284e9b (tag: V.1, main) Modified file2, added .gitignore
* 6898aca (HEAD) Modified file1 and added file2 (in dir1)
* 1401d56 Initial repo and added file1
Notice that file2 is now also absent. If we list the files that are part of the repo:
git ls-files
dir1/file2
file1
we will see that we are back to the state where only file is present
Another visual representation of the repository
Rstudio git history representation
If we go to the “History” tab of the “Review Changes” window, you will notice that the commit history has been truncated to reflect that we have gone back in commit history.
Nevertheless, if we select “All branches” from the dropdown menu, we can see the full commit history.
The output advises us that we are in a detached HEAD state. This occurs when a commit is checked out rather than a branch. Normally, when changes are committed, the new commit is added to the HEAD of the current branch. However, in a detached HEAD state, any commits that are made are not associated with any branch and will effectively be lost next time you checkout.
So if for example, we then added another file (file3)..
echo'END'> file3git add file3git commit -m'END added to file3'
[detached HEAD 0c83489] END added to file3
1 file changed, 1 insertion(+)
create mode 100644 file3
More information about this repository
git reflog
0c83489 HEAD@{0}: commit: END added to file3
6898aca HEAD@{1}: checkout: moving from main to 6898a
6898aca HEAD@{2}: reset: moving to 6898aca
b284e9b HEAD@{3}: reset: moving to V.1
6898aca HEAD@{4}: reset: moving to 6898aca
b284e9b HEAD@{5}: commit: Modified file2, added .gitignore
6898aca HEAD@{6}: commit: Modified file1 and added file2 (in dir1)
1401d56 HEAD@{7}: commit (initial): Initial repo and added file1
git log --graph --decorate --oneline --all
* 0c83489 (HEAD) END added to file3
| * b284e9b (tag: V.1, main) Modified file2, added .gitignore
|/
* 6898aca Modified file1 and added file2 (in dir1)
* 1401d56 Initial repo and added file1
Now if we checked out main, the commit we made whilst in detached head mode would be lost.
git checkout main
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
0c83489 END added to file3
If you want to keep it by creating a new branch, this may be a good time
to do so with:
git branch <new-branch-name> 0c83489
Switched to branch 'main'
More information about this repository
git reflog
b284e9b HEAD@{0}: checkout: moving from 0c83489041cbfe25707f8a2823a1304b4e0a2954 to main
0c83489 HEAD@{1}: commit: END added to file3
6898aca HEAD@{2}: checkout: moving from main to 6898a
6898aca HEAD@{3}: reset: moving to 6898aca
b284e9b HEAD@{4}: reset: moving to V.1
6898aca HEAD@{5}: reset: moving to 6898aca
b284e9b HEAD@{6}: commit: Modified file2, added .gitignore
6898aca HEAD@{7}: commit: Modified file1 and added file2 (in dir1)
1401d56 HEAD@{8}: commit (initial): Initial repo and added file1
git log --graph --decorate --oneline --all
* b284e9b (HEAD -> main, tag: V.1) Modified file2, added .gitignore
* 6898aca Modified file1 and added file2 (in dir1)
* 1401d56 Initial repo and added file1
If, having reviewed the state of a commit (by checking it out), we decided that we wanted to roll back to this state and develop further (make additional commits), we are effectively deciding to start a new branch that splits off at that commit. See the section on Branching for more details on how to do that.
10 Synching with remote repository
When a project has multiple contributors, it is typical for there to be a remote repository against which each contributor can exchange their contributions. The remote repository comprises only the .git folder (and its contents), it never has a workspace. Files are rarely edited directly on the remote repository. Instead, it acts as a constantly available ‘main’ conduit between all contributors.
A remote repository can be anywhere that you have permission to at least read from. Obviously, if you also want to contribute your local commits to the remote repository, you also need write access to that location. If you intend to collaborate, then the remote repository also needs to be in a location that all users can access at any time.
For this demonstration, we will start by re-generating a repository that we made earlier on in this tutorial. This repository comprises a main branch along with an un-merged Feature branch.
Initialized empty Git repository in /home/runner/tmp/Repo1/.git/
[main (root-commit) 5cdb90a] Initial repo and added file1
1 file changed, 1 insertion(+)
create mode 100644 file1
[main 8a7c777] Modified file1 and added file2 (in dir1)
2 files changed, 2 insertions(+)
create mode 100644 dir1/file2
[main 3b0c29d] Modified file2, added .gitignore
2 files changed, 2 insertions(+)
create mode 100644 .gitignore
Switched to branch 'Feature'
[Feature 4fc0c5f] New feature
2 files changed, 2 insertions(+)
create mode 100644 dir1/file3
Switched to branch 'main'
[main feb72a7] Bug fix in file1
1 file changed, 1 insertion(+)
Switched to branch 'Feature'
[Feature dcd4b57] Feature complete
1 file changed, 1 insertion(+)
Switched to branch 'main'
feb72a7 HEAD@{0}: checkout: moving from Feature to main
dcd4b57 HEAD@{1}: commit: Feature complete
4fc0c5f HEAD@{2}: checkout: moving from main to Feature
feb72a7 HEAD@{3}: commit: Bug fix in file1
3b0c29d HEAD@{4}: checkout: moving from Feature to main
4fc0c5f HEAD@{5}: commit: New feature
3b0c29d HEAD@{6}: checkout: moving from main to Feature
3b0c29d HEAD@{7}: commit: Modified file2, added .gitignore
8a7c777 HEAD@{8}: commit: Modified file1 and added file2 (in dir1)
5cdb90a HEAD@{9}: commit (initial): Initial repo and added file1
* dcd4b57 (Feature) Feature complete
* 4fc0c5f New feature
| * feb72a7 (HEAD -> main) Bug fix in file1
|/
* 3b0c29d Modified file2, added .gitignore
* 8a7c777 Modified file1 and added file2 (in dir1)
* 5cdb90a Initial repo and added file1
10.1 Simulate a remote repository locally
For the purpose of this tutorial, we will create a remote repository that is on the same computer as the above repository that we have been working on. Whilst not the typical situation, it does mean that an external location and account is not necessary to follow along with the tutorial. As previously mentioned, the actual location of the remote repository is almost irrelevant to how you interact with it. Therefore, whether the remote repository is on the same computer or elsewhere in the world makes little difference (other than permissions and connections).
This step is not supported directly by Rstudio - please use the terminal.
cd ~/tmp/RemoteRepo1git init --bare
Initialized empty Git repository in /home/runner/tmp/RemoteRepo1/
Now that we have a remote repository - albeit empty at this stage - we return to our local repository and declare (add) the location of the remote repository using the git remote add <name> <url>command. In this command, an optional name can be supplied to refer to the remote repository (<name>). The compulsory <url>argument is the address (location) of the remote repository.
This step is not supported directly by Rstudio - please use the terminal.
git remote add origin ~/tmp/RemoteRepo1
To see what this has achieved, we can have a quick look at the .git/config
You should notice that there is now a ‘remote’ section with the name of ‘origin’ and the ‘url’ points to the location we nominated.
10.1.1 Pushing
Currently the remote repository is empty. We will now push our local commit history to the remote repository. This is achieved via the git push -u <name> <ref>command. Here, <name> is the name of the remote repository (‘origin’) and <ref> is a reference the head of the commit chain we want to sync.
To /home/runner/tmp/RemoteRepo1
* [new branch] main -> main
branch 'main' set up to track 'origin/main'.
Rstudio does not have a direct means by which we can define the remote repository. Thus, we must start by entering the following into the terminal.
git push -u origin main
Thereafter, you might notice that some up and down (push and pull respectively) buttons become active within the “git” panel.
Now, after each subsequent commit, you can “push” your code to the remote repository simply by pushing the up (push) arrow.
git refloggit log --graph--decorate--oneline--all
feb72a7 HEAD@{0}: checkout: moving from Feature to main
dcd4b57 HEAD@{1}: commit: Feature complete
4fc0c5f HEAD@{2}: checkout: moving from main to Feature
feb72a7 HEAD@{3}: commit: Bug fix in file1
3b0c29d HEAD@{4}: checkout: moving from Feature to main
4fc0c5f HEAD@{5}: commit: New feature
3b0c29d HEAD@{6}: checkout: moving from main to Feature
3b0c29d HEAD@{7}: commit: Modified file2, added .gitignore
8a7c777 HEAD@{8}: commit: Modified file1 and added file2 (in dir1)
5cdb90a HEAD@{9}: commit (initial): Initial repo and added file1
* dcd4b57 (Feature) Feature complete
* 4fc0c5f New feature
| * feb72a7 (HEAD -> main, origin/main) Bug fix in file1
|/
* 3b0c29d Modified file2, added .gitignore
* 8a7c777 Modified file1 and added file2 (in dir1)
* 5cdb90a Initial repo and added file1
Another visual representation of the repository
Note that when we pushed the commits to the remote repository, we only pushed the main branch. Consequently, the remote repository only has a single branch.
10.2 Cloning
To collaborate with others on a repository, we start by cloning the repository you wish to collaborate on. So at the moment, we have the original repository (~/tmp/Repo1) created by user 1. We also have a remote repository (~/tmp/RemoteRepo1).
To demonstrate cloning (and collaborating), we will also assume the personal of user 2 and we will clone the remote repository to yet another local path (~/tmp/MyRepo1). Of course, this would not normally be on the same machine as the original repository, we are just doing it this way to simulate multiple users on the same machine.
click on the Project selector in the top right of the Rstudio window (as highlighted by the red ellipse in the image below.
select New Project from the dropdown menu
select Version Control form the Create Project panel
select Git from the Create Project from Version Control panel
provide a path to a remote repository. Normally this URL would be for a location on a server such as Github, Gitlab, Bitbucket etc.
However, for this demostration we will point to the remote repository that we set up in the previous section (~/tmp/RemoteRepo1)
provide a directory name in which to store this new cloned repository. Normally this field is populated based on the name give in the URL. However, in this case, it would suggest a name of RemoteRepo1 which already exists (for the repository we are trying to clone), and we don’t wish to overwrite that one. I will instead offer an alternative name (MyRepo1).
we also need to supply a path to where this cloned repository will be stored.
click the “Create Project” button.
The contents (and state) of ~/tmp/MyRepo1 should match that of ~/tmp/Repo1 (other than any files excluded due to a .gitignore or files not yet committed).
Note that when cloning repository, all branches in the remote repository are cloned. However, since the remote repository only had one branch (main), so too the clone only has one branch.
Now as the collaborator (user 2), lets make a modification and push this change up to the remote repository.
Important info about pushing to a remote repository
Before pushing any changes, it is absolutely vital that you adhere to the following steps:
commit your changes - so that you have something new to push and they are safe before the next step.
pull (and if necessary reconcile - see the next section below) the latest from the remote repository. This is critical as it ensures that the changes you are pushing are against the latest stage of the repository. Without this step, you might be pushing changes that are based on a stage that is not longer current.
To /home/runner/tmp/RemoteRepo1
feb72a7..24abf69 main -> main
branch 'main' set up to track 'origin/main'.
start by pulling the latest from the remote repository just incase there has been an change
click on the “Create new blank file in the current directory” button and select “Text file” - name it file4
edit this file by adding the contents Something else
save the file
stage (add) the file
commit the change with a message of “Added file4”
push this commit either by clicking on the green up (push) arrow in the “Review Changes” window or the same arrow in the git tab of the main Rstudio window.
Notice how the second (cloned. MyRepo1) repository and the remote repository (RemoteRepo1) are one commit ahead of the original local repository (Repo1). For Repo1 to be in sync with MyRepo1, the original user will have to pull the remote repository changes manually.
10.3 Pulling
Retrieving a commit chain (pulling) from a remote repository is superficially the opposite of pushing. Actually, technically it is two actions:
a fetch that retrieves the remote information and uses it to create a branch off your local repository (the name of this branch is made from the name of the remote and the branch that was fetched - e.g. origin/master).
a merge that merges this branch into the main repository.
These actions can be performed individually, however, they are more typically performed together via the git pullcommand.
To illustrate, lets return to being user 1 and we will pull the changes contributed by user 2 in the section above.
The associated message informs us that upon pulling, a file (file4) has been added. Any conflicts arising from the merging stage of the pull can be resolved in the usual manner of opening the conflicted file(s) making manual edits and then committing the changes.
git reflog
24abf69 HEAD@{0}: pull: Fast-forward
feb72a7 HEAD@{1}: checkout: moving from Feature to main
dcd4b57 HEAD@{2}: commit: Feature complete
4fc0c5f HEAD@{3}: checkout: moving from main to Feature
feb72a7 HEAD@{4}: commit: Bug fix in file1
3b0c29d HEAD@{5}: checkout: moving from Feature to main
4fc0c5f HEAD@{6}: commit: New feature
3b0c29d HEAD@{7}: checkout: moving from main to Feature
3b0c29d HEAD@{8}: commit: Modified file2, added .gitignore
8a7c777 HEAD@{9}: commit: Modified file1 and added file2 (in dir1)
5cdb90a HEAD@{10}: commit (initial): Initial repo and added file1
git log --graph--decorate--oneline--all
* 24abf69 (HEAD -> main, origin/main) Added file4
* feb72a7 Bug fix in file1
| * dcd4b57 (Feature) Feature complete
| * 4fc0c5f New feature
|/
* 3b0c29d Modified file2, added .gitignore
* 8a7c777 Modified file1 and added file2 (in dir1)
* 5cdb90a Initial repo and added file1
git reflog
24abf69 HEAD@{0}: commit: Added file4
feb72a7 HEAD@{1}: clone: from /home/runner/tmp/RemoteRepo1
git log --graph--decorate--oneline--all
* 24abf69 (HEAD -> main, origin/main, origin/HEAD) Added file4
* feb72a7 Bug fix in file1
* 3b0c29d Modified file2, added .gitignore
* 8a7c777 Modified file1 and added file2 (in dir1)
* 5cdb90a Initial repo and added file1
git refloggit log --graph--decorate--oneline--all
* 24abf69 (HEAD -> main) Added file4
* feb72a7 Bug fix in file1
* 3b0c29d Modified file2, added .gitignore
* 8a7c777 Modified file1 and added file2 (in dir1)
* 5cdb90a Initial repo and added file1
10.4 Github as a remote repository
GitHub provides the world’s leading centralized platform for version control, collaboration, and project management, facilitating seamless teamwork, tracking changes, and ensuring the integrity and accessibility of code repositories throughout the software development lifecycle.
Although anyone can explore (read) public repositories on github, only those with github accounts can contribute and collaborate.
register by providing your prefered email address, a username and a password when prompted
to complete the account activation, you will need to verify your details via an email sent to your nominated email address
As of the start of 2024, github now requires Two-Factor Authentication (2FA) for enhanced security. Whenever you login to github (or are prompted for a password, you will also need to use 2FA. To setup 2FA:
click on your profile picture in the top right corner.
select “Settings” from the dropdown menu.
select “Password and authentication” in the left sidebar.
under “Two-factor authentication” section, click “Enable”.
choose your preferred method (authenticator app or SMS) and follow the prompts to set it up.
Passwords and Two-Factor Authentication (2FA) are used when you (as a human) securely login and interact directly with the GitHub website. However, it is also possible to have other tools (such as git) interact with Github on your behalf via an Application Programming Interfacet (API). Passwords/2FA are not appropriate to authenticate these machine to machine communications. Instead, Github requires the use of a Personal Access Token (PAT). PATs offer a more secure and granular approach, allowing users to control access without exposing their account password.
To generate a Personal Access Token (PAT):
click on your profile picture in the top right corner.
select “Settings” from the dropdown menu.
select “Developer settings” from the bottom of the left sidebar.
select “Personal access tokens” from the left sidebar.
select “Tokens (classic)” from the dropdown menu
click “Generate new token”
select “Generate new token (classic)” from the dropdown menu
at this point you will likely be prompted for your password
provide a “note” - this is more of a short description of what the token is to be used for (in the example below, I have entered “git push/pull” to remind me that this is a simple token for regular push/pull interaction between my local and remote repositories).
You also need to provide an expiration. Although not secure or recommended, I have selected “No expiration” as I don’t want to have to re-do my PAT across multiple machines too regularly.
Finally, you also need to indicate scope (what activities you are granting permission for the tools to be able to perform). In this case, I have ticked the “repo” box. This grants general rea/write access to my repositories. I have not granted permission for more administration like activities such as managing teams, deleting repositories, etc - these activities I am happy to perform myself via the website.
click “Generate token” and securely copy the generated token. Until this is stored safely (see below) do not close the page, because Github will never show you this PAT again.
Important
Important: Store your PAT safely as you won’t be able to see it again! Ideally, you should store this PAT in a digital wallet. Digital wallets vary according to operating systems. R users might like to use the r function from the asdf package (which you will need to install prior) as follows in order to store the PAT.
In an R console, enter:
gitcreds::gitcreds_set()
When propted for a password, paste in the copied PAT that hopefully is still in your clipboard - else you might need to re-copy it.
To confirm that you have successfully stored your PAT in your wallet, you can:
gitcreds::gitcreds_get()
and confirm that it indicates that there is a hidden password.
10.4.2 Create remote Github repository
login to your Github account
either:
click on the “Create new..” button (with the plus sign) to the right of your profile picture in the top right corner and select “New repository” from the dropdown menu
click on “Repositories” from the top horizontal menu followed by the big green “New” button
fill out the details of the Create a new repository for similar to the following
In particular:
give the repository a name. Typically use the same name as you used for the local repository to avoid confusion
provide a description. Along with the name, this field is searchable so the more detailed it is, the more likely your repository will be discoverable by others as well as yourself in the future
indicate the privacy level. This affects whether your repository is discoverable and readable by anyone (public) or just those you invite (private)
ideally, you also want to include a README file and license in your repository. However, if you enable either of these options in the form, Github will bypass providing a screen with some additional instructions that many find useful for linking your local and remote repository. So on this occasion, we will leave these options as they are
click the “Create repository” button at the bottom of the page
Github will present you with the following page:
This page presents three alternative sets of instructions that you run locally (on your machine) in order to establish a link between the local and remote repository. You need to run the appropriate set of commands in your local terminal
if no local repository exists, follow the first set of instructions
if you already have a local repository (as is the case with this demonstration), follow the second set of instructions
if you intend to import a repository from a different versioning system, follow the last set of instructions
once you have run the above commands locally, you can refresh the Github page and you will be presented with your remote repository. From here you can navigate through your code, manage privileges etc.
If you would like to allow others to collaborate with you on your repository, then regardless of whether the repository is public or private, you will need to invite them as a collaborator. To do so:
click on “Settings” from the horizontal menu bar
click on “Collaborators” from the left sidebar (you may then be asked to submit your password)
click on the green “Add people” button
in the popup, enter either the username, full name or email address of the person you want to invite to collaborate with you. Once you click the “Select a collaborator above” and select the appropriate candidate, this person will be sent an invite via email.
nominate the role that this collaborator can assume (e.g. what capacity does the collaborator have to edit, invite others, alter settings, delete the repository etc)
repeat steps 3-4 for each additional collaborator you wish to invite
11 Resolving conflicts
In Git, conflicts arise when changes made in different branches cannot be automatically merged. This typically happens when two branches modify the same part of a file and the changes overlap. Think of it like two writers revising the same sentence differently. Conflicts usually arise when merging or rebasing branches.
When git identifies a conflict, it will mark the conflicting areas (with sets of plain text fences - see below), and it’s then up to the user to resolve them manually by making edits the conflicted file(s) and choosing which changes to keep. After resolving conflicts, the changes can be staged, and the merge or rebase can be completed. Conflict resolution is an essential skill in collaborative Git workflows, ensuring smooth integration of changes from multiple contributors.
Recall that pulling from a remote repository is a to stage process. Firstly the new commits are “fetched” to a new temporary branch and then this branch is merged into the local repository. Hence, conflicts can occur when pulling from remote repositories. Conflict resolution in such cases is as outlined above.
To illustrate a git conflict, we will start with a very simple repository, create a branch and then concurrently make edits to the same part of the same file on each branch before attempting to merge the branches together.
Commands to create the repository
rm-rf ~/tmp/Repo1mkdir ~/tmp/Repo1cd ~/tmp/Repo1git init echo'File 1'> file1git add file1git commit -m'Initial repo and added file1'echo'---------------'>> file1mkdir dir1echo'* Notes'> dir1/file2git add file1 dir1/file2git commit -m'Modified file1 and added file2 (in dir1)'echo'---'>> dir1/file2echo'temp'> dir1/f.tmpecho'*.tmp'> .gitignoregit add .git commit -m'Modified file2, added .gitignore'git branch Featuregit checkout Featureecho'some text added on Feature branch'>> file1echo'File 3'> dir1/file3git add .git commit -m'New feature'git checkout mainecho' a bug fix on the main branch'>> file1git add .git commit -m'Bug fix in file1'git refloggit log --graph--decorate--oneline--all
Initialized empty Git repository in /home/runner/tmp/Repo1/.git/
[main (root-commit) 2b9ba19] Initial repo and added file1
1 file changed, 1 insertion(+)
create mode 100644 file1
[main e97dcbb] Modified file1 and added file2 (in dir1)
2 files changed, 2 insertions(+)
create mode 100644 dir1/file2
[main f267fa7] Modified file2, added .gitignore
2 files changed, 2 insertions(+)
create mode 100644 .gitignore
Switched to branch 'Feature'
[Feature 87647d8] New feature
2 files changed, 2 insertions(+)
create mode 100644 dir1/file3
Switched to branch 'main'
[main 419ec60] Bug fix in file1
1 file changed, 1 insertion(+)
419ec60 HEAD@{0}: commit: Bug fix in file1
f267fa7 HEAD@{1}: checkout: moving from Feature to main
87647d8 HEAD@{2}: commit: New feature
f267fa7 HEAD@{3}: checkout: moving from main to Feature
f267fa7 HEAD@{4}: commit: Modified file2, added .gitignore
e97dcbb HEAD@{5}: commit: Modified file1 and added file2 (in dir1)
2b9ba19 HEAD@{6}: commit (initial): Initial repo and added file1
* 87647d8 (Feature) New feature
| * 419ec60 (HEAD -> main) Bug fix in file1
|/
* f267fa7 Modified file2, added .gitignore
* e97dcbb Modified file1 and added file2 (in dir1)
* 2b9ba19 Initial repo and added file1
Auto-merging file1
CONFLICT (content): Merge conflict in file1
Automatic merge failed; fix conflicts and then commit the result.
Merging is not directly supported by Rstudio, please use the terminal.
Hmmm. It appears that there is a conflict (although we should not be supprised since we deliberately set the repository up to have a conflict!). If we explore the a git diff, we will see that on the master and Featurebranches have incompatible changes.
More information about the conflict
git status
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Changes to be committed:
new file: dir1/file3
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: file1
The above status informs us that whilst we were able to successfully merge in the changes in dir1/file3, the modifications in file1 remain unmerged (due to conflicts).
If we run a git diff on the two branches, we will be able to identify all the differences within all comparable files between the two branches.
git diff main Feature
diff --git a/dir1/file3 b/dir1/file3
new file mode 100644
index 0000000..8cf9e18
--- /dev/null
+++ b/dir1/file3
@@ -0,0 +1 @@
+File 3
diff --git a/file1 b/file1
index 11c945a..8a32d91 100644
--- a/file1
+++ b/file1
@@ -1,3 +1,3 @@
File 1
---------------
- a bug fix on the main branch
+some text added on Feature branch
The output indicates that dir/file3 does not exist on the main branch - this is not a conflict. However, for file1, we see that line three differs between the two branches.
It is not so much that they have both made changes to the same file, it is more that the changes are to the same part of the file. Lets look at the contents of file1 in the commit that is the common ancester of both branches:
git cat-file -p main^:file1
File 1
---------------
So prior to branching, the file1 file had two lines of text.
In the latest commit on the main branch, the file1 has added a third line with text about a bug fix.
git cat-file -p main:file1
File 1
---------------
a bug fix on the main branch
Yet on the Feature branch, the third line of the file1 file contains some text added on Feature branch.
git cat-file -p Feature:file1
File 1
---------------
some text added on Feature branch
So the source of this conflict evidently is the third line of the file1 file.
We can see that the changes made to file1 are inconsistent. We need to decide which edits (if any) we want to use. Recall that the change made in main was to address a bug or issue. Perhaps this bug or issue does not arise with the new Feature and thus is superfluous. Alternatively, it might be that this bug fix is required by both branches (if so, we probably should have introduced it to the Featurebranch at the same time as the main anyway….
Lets address the conflict by rolling back file1 from the mainbranch.
The following figures depict the file1 file with conflicts (left) and once resolved (right).
Normally we would resolve this issue using a code editor to edit the actual changes in the file back to the desired condition. However as we only have the one change and this demo is fully scripted, I will instead roll back (checkout) this one file from the earlier commit on the main branch.
git checkout main^ file1git add .git commit -m'Merge in Feature'
Updated 1 path from 3c7af0d
[main ca2b6e7] Merge in Feature
open file1 for editing
make the necessary changes to the text
save the file
stage (add) the file (you may have to click on the checkbox twice before the tick appears)
commit the changes with a message like “Merge in Feature”
More information about this repository
git reflog
ca2b6e7 HEAD@{0}: commit (merge): Merge in Feature
419ec60 HEAD@{1}: commit: Bug fix in file1
f267fa7 HEAD@{2}: checkout: moving from Feature to main
87647d8 HEAD@{3}: commit: New feature
f267fa7 HEAD@{4}: checkout: moving from main to Feature
f267fa7 HEAD@{5}: commit: Modified file2, added .gitignore
e97dcbb HEAD@{6}: commit: Modified file1 and added file2 (in dir1)
2b9ba19 HEAD@{7}: commit (initial): Initial repo and added file1
git log --graph --decorate --oneline --all
* ca2b6e7 (HEAD -> main) Merge in Feature
|\
| * 87647d8 (Feature) New feature
* | 419ec60 Bug fix in file1
|/
* f267fa7 Modified file2, added .gitignore
* e97dcbb Modified file1 and added file2 (in dir1)
* 2b9ba19 Initial repo and added file1
Source Code
---title: Git and version controlauthor: "Murray Logan"date: "`r format(Sys.time(), '%d %B, %Y')`"format: html: toc: true toc-float: true page-layout: full number-sections: true number-depth: 3 embed-resources: true code-fold: false code-tools: true code-summary: "Show the code" code-line-numbers: true code-block-border-left: "#ccc" code-copy: true highlight-style: atom-one theme: [default, ../resources/tut-style.scss] css: ../resources/tut-style.csscrossref: fig-title: '**Figure**' fig-labels: arabic tbl-title: '**Table**' tbl-labels: arabicengine: knitrbibliography: ../resources/references.biboutput_dir: "docs"---```{r setup, include=FALSE,warning=FALSE, cache=FALSE, message=FALSE}knitr::opts_chunk$set(echo = TRUE,warning=FALSE, message=FALSE, cache = TRUE, comment = "")options(tinytex.engine = 'xelatex')cleanRmdInput <- function(x) {#x <- gsub("```\\{r","```markdown\n`r ''```\\{r",x)x <- gsub("^```$","`` `",x) # the Makefile will then change this back to ``` after pandocx}library(tidyverse)library(pander)FIG_PATH <- '10_git_files/figure-html/'```::: {.newsbox}**Other useful tutorials or resources**- [https://git-scm.com/book/en/v2](https://git-scm.com/book/en/v2)- [https://www.atlassian.com/git/tutorials](https://www.atlassian.com/git/tutorials)- [https://marklodato.github.io/visual-git-guide/index-en.html](https://marklodato.github.io/visual-git-guide/index-en.html)- [https://git-scm.com/docs/gittutorial](https://git-scm.com/docs/gittutorial)- [https://marklodato.github.io/visual-git-guide/index-en.html](https://marklodato.github.io/visual-git-guide/index-en.html)- [https://try.github.io/levels/1/challenges/1](https://try.github.io/levels/1/challenges/1)- [https://onlywei.github.io/explain-git-with-d3/](https://onlywei.github.io/explain-git-with-d3/)- [http://git-school.github.io/visualizing-git/](http://git-school.github.io/visualizing-git/)- [https://github.com/sensorflo/git-draw](https://github.com/sensorflo/git-draw):::This tutorial will take a modular approach. The first section willprovide an overview of the basic concepts of git. The second sectionwill provide a quick overview of basic usage and the third and finalsection will cover intermediate level usage. In an attempt to easeunderstanding, the tutorial will blend together git commands andoutput, schematic diagrams and commentary in an attempt to easeunderstanding.The following table surves as both a key and overview of the mostcommon actions and git 'verbs'.```{cat}#| label: common#| echo: true#| eval: true#| cache: true#| engine.opts:#| file: "../resources/common.tikz"\tikzstyle{TARGET} = [font={\fontspec[Scale=2]{NotoSans-Regular}}]\tikzstyle{hashText} = [font={\fontspec[Scale=1.5]{Inconsolata}}]\tikzstyle{commentText} = [font={\fontspec[Scale=1.0]{Inconsolata}}]\tikzstyle{refText} = [font={\fontspec[Scale=1.5]{Inconsolata}}]\definecolor{color_branch}{rgb}{1,0.8,0.4}\definecolor{color_head}{HTML}{6495ED} %\definecolor{color_HEAD}{rgb}{0.26,0.65,0.91}\definecolor{color_commit}{rgb}{0.9,0.9,0.2}\definecolor{color_inactive}{rgb}{0.90,0.9,0.9}\definecolor{color_detached}{rgb}{0.90,0.9,0.9}\definecolor{color_derivative}{rgb}{0.12,0.6,0.51}\definecolor{color_local}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}% A template for making the storage symbol\newcommand{\state}[3]{\draw (#1) node [draw=none,fill=#2,shape=circle,minimum width=2cm] (#3) {\begin{tikzpicture}\node [draw=white, fill=white,shape=cylinder,shape aspect=1.3 ,shape border rotate=90,minimum height=1.6cm,minimum width=1.5cm] at (0,0) (Cylinder) {};\draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.1cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.5cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.9cm)$) arc (-180:0:0.8cm and 0.2cm); \end{tikzpicture}};}\newcommand{\commit}[5]{\node [#1,inner sep=0,outer sep=0] (#2) {\begin{tikzpicture}[#1]\draw [#1] node [draw=black!40!#3,fill=#3,shape=circle,minimum width=1.0cm,line width=3pt] (x) {};%\node [below,hashText] at (x.south) {#4};\end{tikzpicture}};\ifx\notempty#4\empty\node [below,hashText, minimum height = 0] at (#2.south) {#4};\fi\ifx\notempty#5\empty\node [below,commentText,text=gray, minimum height = 0] at ($(#2.south) +(0,-0.5)$) {#5};\fi}\newcommand{\rcommit}[5]{\node [#1,inner sep=0,outer sep=0] (#2) {\begin{tikzpicture}[#1]\draw [#1] node [draw=black!40!#3,fill=#3,shape=rectangle,minimum width=1.0cm,minimum height=1.0cm,line width=3pt] (x) {};%\node [below,hashText] at (x.south) {#4};\end{tikzpicture}};\node [below,hashText] at (#2.south) {#4};\node [below,commentText,text=gray] at ($(#2.south) +(0,-0.5)$) {#5};}\newcommand{\master}[1] {\node [#1,rectangle,fill=color_branch,draw=black!20!color_branch,line width=2pt,refText,minimum height=0.8cm] (master) {main};}\newcommand{\rmaster}[1] {\node [#1,rectangle,fill=color_branch,draw=black!20!color_branch,line width=2pt,refText,minimum height=0.8cm] (rmaster) {origin/main};}\newcommand{\branch}[2] {\node [#1,rectangle,fill=color_branch,draw=black!20!color_branch,line width=2pt,refText,minimum height=0.8cm] (#2) {#2};}\newcommand{\HEAD}[1] {\node [#1,rectangle,fill=color_HEAD,draw=black!20!color_HEAD,line width=2pt,refText,minimum height=0.8cm] (HEAD) {HEAD};}\newcommand{\rHEAD}[1] {\node [#1,rectangle,fill=color_HEAD,draw=black!20!color_HEAD,line width=2pt,refText,minimum height=0.8cm] (rHEAD) {origin/HEAD};}``````{tikz}%| label: Fig0%| engine: tikz%| echo: false%| cache: true%| include: false%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\begin{tikzpicture}%[every node/.style={inner sep=0,outer sep=0, minimum height = 0}]\commit{}{A}{white}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\end{tikzpicture}``````{bash}#| label: Fig0-conv#| warning: false#| message: false#| include: false#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Fig0-1.pdf 10_git_files/figure-html/Fig0-1.png#magick -density 300 10_git_files/figure-html/Fig0-1.pdf -resize 20% -trim +repage 10_git_files/figure-htm/Fig0-1.png``````{tikz}%| label: Fig1%| engine: tikz%| echo: false%| cache: true%| include: false%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\begin{tikzpicture}[every node/.style={inner sep=0,outer sep=0, minimum height = 0}]\commit{}{A}{color_commit!30}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\end{tikzpicture}``````{bash}#| label: Fig1-conv#| warning: false#| message: false#| include: false#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Fig1-1.pdf 10_git_files/figure-html/Fig1-1.png``````{tikz}%| label: Fig2%| engine: tikz%| echo: false%| cache: true%| include: false%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\begin{tikzpicture}[every node/.style={inner sep=0,outer sep=0, minimum height = 0}]\commit{}{A}{color_commit}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\end{tikzpicture}``````{bash}#| label: Fig2-conv#| warning: false#| message: false#| include: false#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Fig2-1.pdf 10_git_files/figure-html/Fig2-1.png``````{tikz}%| label: Fig3a%| engine: tikz%| echo: false%| cache: true%| include: false%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\begin{tikzpicture}%[every node/.style={inner sep=0,outer sep=0, minimum height = 0}]\commit{}{A}{color_inactive}{}{}\commit{right = 1cm of A}{B}{color_commit}{}{}\commit{right = 1cm of B}{C}{color_detached}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\draw [-,line width=3pt,draw=black!60] (B) -- (A);\end{tikzpicture}``````{bash}#| label: Fig3a-conv#| warning: false#| message: false#| include: false#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Fig3a-1.pdf 10_git_files/figure-html/Fig3a-1.png ``````{tikz}%| label: Fig3b%| engine: tikz%| echo: false%| cache: true%| include: false%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\begin{tikzpicture}%[every node/.style={inner sep=0,outer sep=0, minimum height = 0}]\commit{}{A}{color_inactive}{}{}\commit{right = 1cm of A}{B}{color_commit}{}{}\commit{right = 1cm of B}{C}{color_detached!30}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\draw [-,line width=3pt,draw=black!60] (B) -- (A);\draw [->,dashed,line width=3pt,draw=black!60] (C) -- (B);\end{tikzpicture}``````{bash}#| label: Fig3b-conv#| warning: false#| message: false#| include: false#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Fig3b-1.pdf 10_git_files/figure-html/Fig3b-1.png ``````{tikz}%| label: Fig3c%| engine: tikz%| echo: false%| cache: true%| include: false%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\begin{tikzpicture}%[every node/.style={inner sep=0,outer sep=0, minimum height = 0}]\commit{}{A}{color_inactive}{}{}\commit{right = 1cm of A}{B}{color_inactive}{}{}\commit{right = 1cm of B}{C}{color_commit}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\draw [-,line width=3pt,draw=black!60] (B) -- (A);\draw [-,line width=3pt,draw=black!60] (C) -- (B);\draw [->,line width=3pt,draw=black!60] (A) to[out=45] (C);\end{tikzpicture}``````{bash}#| label: Fig3c-conv#| warning: false#| message: false#| include: false#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Fig3c-1.pdf 10_git_files/figure-html/Fig3c-1.png ``````{tikz}%| label: Fig4%| engine: tikz%| echo: false%| cache: true%| include: false%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\tikzstyle{refText} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}]\begin{tikzpicture}%[every node/.style={inner sep=0,outer sep=0, minimum height = 0}]\commit{}{A}{color_inactive}{}{}\commit{right = 1cm of A}{B}{color_inactive}{}{}\commit{right = 1cm of B}{C}{color_commit}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\draw [-,line width=3pt,draw=black!60] (B) -- (A);\draw [-,line width=3pt,draw=black!60] (C) -- (B);\commit{above = 0.5cm of B}{D}{color_branch}{}{}\draw [-,line width=3pt,draw=black!60] (A.east) to[out=0,in=180] (D);%\node[right = 0.5cm of C, rectangle, refText] (master) {main};\master{right = 0.5cm of C}\draw[->,line width=3pt,draw=black!60] (master) -- (C);\branch{right = 0.5cm of D}{Feature} \draw[->,line width=3pt,draw=black!60] (Feature) -- (D);\end{tikzpicture}``````{bash}#| label: Fig4-conv#| warning: false#| message: false#| include: false#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Fig4-1.pdf 10_git_files/figure-html/Fig4-1.png ``````{tikz}%| label: Fig5%| engine: tikz%| echo: false%| cache: true%| include: false%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\tikzstyle{refText} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}]\begin{tikzpicture}%[every node/.style={inner sep=0,outer sep=0, minimum height = 0}]\commit{}{A}{color_inactive}{}{}\commit{right = 1cm of A}{B}{color_inactive}{}{}\commit{right = 1cm of B}{C}{color_commit}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\draw [-,line width=3pt,draw=black!60] (B) -- (A);\draw [-,line width=3pt,draw=black!60] (C) -- (B);\commit{above = 0.5cm of B}{D}{color_inactive}{}{}\draw [-,line width=3pt,draw=black!60] (A.east) to[out=0,in=180] (D);\master{right = 0.5cm of C}\draw[->,line width=3pt,draw=black!60] (master) -- (C);\branch{right = 0.5cm of D}{Feature}\draw[->,line width=3pt,draw=black!60] (Feature) -- (D);\draw [-,line width=3pt,draw=black!60] (D.east) to[out=0,in=180] (C);\end{tikzpicture}``````{bash}#| label: Fig5-conv#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Fig5-1.pdf 10_git_files/figure-html/Fig5-1.png ``````{tikz}%| label: Fig6a%| engine: tikz%| echo: false%| cache: true%| include: false%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\tikzstyle{refText} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}]\begin{tikzpicture}%[every node/.style={inner sep=0,outer sep=0, minimum height = 0}]\commit{}{A}{color_inactive}{}{}\commit{right = 1cm of A}{B}{color_detached!30}{}{}\commit{right = 1cm of B}{C}{color_commit}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\draw [-,line width=3pt,draw=black!60] (C) -- (A);\end{tikzpicture}``````{bash}#| label: Fig6a-conv#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Fig6a-1.pdf 10_git_files/figure-html/Fig6a-1.png ``````{tikz}%| label: Fig5b%| engine: tikz%| echo: false%| cache: true%| include: false%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\tikzstyle{refText} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}]\begin{tikzpicture}%[every node/.style={inner sep=0,outer sep=0, minimum height = 0}]\commit{}{A}{color_commit}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);%\commit{right = 1cm of A}{B}{color_commit}{}{}%\draw [-,line width=3pt,draw=black!60] (B) -- (A);\master{right = 0.5cm of A}\draw[->,line width=3pt,draw=black!60] (master) -- (A);\rcommit{below = 1cm of A}{rA}{color_inactive}{}{}\draw [-,line width=3pt,draw=black!60] (rA) -- ++(-1,0);\rcommit{right = 1cm of rA}{rB}{color_commit}{}{}\draw [-,line width=3pt,draw=black!60] (rB) -- (rA);\rmaster{right = 0.5cm of rB}\draw[->,line width=3pt,draw=black!60] (rmaster) -- (rB);\draw [<-,line width=3pt, draw=black!60, dashed] (A) -- (rB);\end{tikzpicture}``````{bash}#| label: Fig5b-conv#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Fig5b-1.pdf 10_git_files/figure-html/Fig5b-1.png ``````{tikz}%| label: Fig5a%| engine: tikz%| echo: false%| cache: true%| include: false%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\tikzstyle{refText} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}]\begin{tikzpicture}%[every node/.style={inner sep=0,outer sep=0, minimum height = 0}]\commit{}{A}{color_inactive}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\commit{right = 1cm of A}{B}{color_commit}{}{}\draw [-,line width=3pt,draw=black!60] (B) -- (A);\master{right = 0.5cm of B}\draw[->,line width=3pt,draw=black!60] (master) -- (B);\rcommit{below = 1cm of A}{rA}{color_commit}{}{}\draw [-,line width=3pt,draw=black!60] (rA) -- ++(-1,0);\rmaster{right = 0.5cm of rA}\draw[->,line width=3pt,draw=black!60] (rmaster) -- (rA);\draw [->,line width=3pt, draw=black!60, dashed] (B) -- (rA);\end{tikzpicture}``````{bash}#| label: Fig5a-conv#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Fig5a-1.pdf 10_git_files/figure-html/Fig5a-1.png```<table class= "table-sm table-borderless" style='border-spacing:20px;'><tr><td><a href="#Initialize">Initialize git</a><br>![](10_git_files/figure-html/Fig0-1.png)</td><td width='40%'>`git init`</td><td>Establish a git repository (within the current path if no path provided)</td></tr><tr><td><a href="#Commit">Staging</a><br> ![](10_git_files/figure-html/Fig1-1.png)</td><td>`git add <file>`<br>where `file` is one or more files to stage</td><td>Staging is indicating which files and their states are to be included in the next commit.</td></tr><tr><td><a href="#Commit">Committing</a><br>![](10_git_files/figure-html/Fig2-1.png)</td><td>`git commit -m "<Commit message>"`<br>where `<Commit message>` is a message to accompany the commit</td><td>Commiting generates a 'snapshot' of the file system.</td></tr><tr> <td> <a href="#checkout">Checkout</a><br>![](10_git_files/figure-html/Fig3a-1.png) </td> <td>`git checkout "<commit>"`<br> where `<commit>` is a reference to a commit to be reviewed </td> <td> Explore the state associated with a specific commit </td></tr><tr> <td> <a href="#reset">Reset</a><br><!-- <img src="10_git_files/figure-html/Fig3b-1.png" class="" alt="" style="transform: scale(0.2);"/> -->![](10_git_files/figure-html/Fig3b-1.png) </td> <td>`git reset --hard "<commit>"`<br> where `<commit>` is a reference to a commit </td> <td> Return to a previous state, effectively erasing subsequent commits.. </td></tr><tr> <td> <a href="#revert">Revert</a><br>![](10_git_files/figure-html/Fig3c-1.png) </td> <td>`git revert "<commit>"`<br> where `<commit>` is a reference to a commit that should be nullified (inverted) </td> <td> Generate a new commit that reverses the changes introduced by a commit thereby effectively rolling back to a previous state (the one prior to the nominated commit) whilst still maintaining full commit history. </td></tr><tr> <td> <a href="#Branching">Branching</a><br>![](10_git_files/figure-html/Fig4-1.png) </td> <td>`git branch <name>`<br>`git checkout <name>`<br> where `<name>` is a reference to a branch name (e.g. 'Feature') </td> <td> Take edits in the project in a new direction to allow for modifications that will not affect the main (master) branch. </td></tr><tr> <td> <a href="#Merging">Merging</a><br>![](10_git_files/figure-html/Fig5-1.png) </td> <td>`git checkout master`<br>`git branch <name>`<br> where `<name>` is a reference to a branch name (e.g. 'Feature') that is to be merged back into `master`. </td> <td> Incorporate changes in a branch into another branch (typically `master`). </td></tr><tr> <td> <a href="#rebase">Rebasing</a><br>![](10_git_files/figure-html/Fig6a-1.png) </td> <td>`git rebase -i HEAD~<number>`<br> where `<number>` is the number of previous commits to squash together with head. </td> <td> Combine multiple commits together into a single larger commit. </td></tr><tr> <td> <a href="#Pulling">Pulling</a><br>![](10_git_files/figure-html/Fig5b-1.png) </td> <td>`git pull -u <remote> <branch>`<br> where `<remote>` is the name of the remote (typically `origin`) and `<branch>` is the branch to sync with remote (typically `master`). </td> <td> Pull changes from a branch of a remote repository. </td></tr><tr> <td> <a href="#Pushing">Pushing</a><br>![](10_git_files/figure-html/Fig5a-1.png) </td> <td>`git push -u <remote> <branch>`<br> where `<remote>` is the name of the remote (typically `origin`) and `<branch>` is the branch to sync with remote (typically `master`). </td> <td> Push changes up to a branch of a remote repository. </td></tr></table># ContextGit is a distributed versioning system. This means that the completecontents and history of a repository (in simplistic terms a repositoryis a collection of files and associated metadata) can be completelyduplicated across multiple locations.No doubt you have previously been working on a file (could be adocument, spreadsheet, script or any other type of file) and got to apoint where you have thought that you are starting to make edits thatsubstantially change the file and therefore have considered saving thenew file with a new name that indicates that it is a new version.```{tikz}%| label: Fig10%| engine: tikz%| echo: false%| cache: true%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}]\tikzstyle{fileText} = [font={\fontspec[Scale=1]{InconsolataSemiCondensed-Regular}}] \definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_index}{rgb}{0.78,0.86,0.27}\definecolor{color_local}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\definecolor{color_master}{rgb}{0.36,0.27,0.87}\definecolor{color_head}{HTML}{6495ED} %\definecolor{color_head}{rgb}{0.26,0.65,0.91}\newcommand{\file}[3] {\coordinate (#3) at (#1);\draw [anchor=top left](#1) rectangle ++(1.5,-2);\draw [fill=color_workspace]($(#1) +(1.5,0)$) -- ++(0,-0.5) --++(-0.5,0.5) -- cycle; \node [anchor=north west,fileText] at ($(#1) +(0,-0.5)$) {#2};}\begin{tikzpicture}\file{0,0}{\begin{minipage}{1.3cm}\textcolor{red}{- item 1}\\\textcolor{red}{- item 2}\end{minipage}}{F1}\node [fileText] at ($(F1) + (0.75,0.3)$) {Version 1};\node [fileText] at ($(F1) + (0.75,-2.3)$) {todo.txt};\file{2,0}{\begin{minipage}{1.3cm}- item 1\\- item 2\\\textcolor{red}{- item 3}\end{minipage}}{F2}\node [fileText] at ($(F2) + (0.75,0.3)$) {Version 2};\node [fileText] at ($(F2) + (0.75,-2.3)$) {todo2.txt};\file{4,0}{\begin{minipage}{1.3cm}- item 1\\- item 2\\- item 3\\\textcolor{red}{- item 4}\end{minipage}}{F3}\node [fileText] at ($(F3) + (0.75,0.3)$) {Version 3};\node [fileText] at ($(F3) + (0.75,-2.3)$) {todo3.txt};\file{6,0}{\begin{minipage}{1.3cm}- item 1\\- item 2\\\textcolor{blue}{- item 4}\end{minipage}}{F4}\node [fileText] at ($(F4) + (0.75,0.3)$) {Version 4};\node [fileText] at ($(F4) + (0.75,-2.3)$) {todo4.txt};\node [fileText] at ($(F4) +(3.5,-1)$) {\begin{minipage}{2.5cm}An example of a poor, adhoc versioning system.\end{minipage}};)\end{tikzpicture}```In the above diagram, new content is indicated in red andmodifications in blue.Whist this approach is ok, it is fairly limited and unsophisticatedapproach to versioning (keeping multiple versions of a file). Firstly,if you edit this file over many sessions and each time save with adifferent name, it becomes very difficult to either keep tract of whatchanges are associated with each version of the file, or the order inwhich the changes were made. This is massively compounded if a projectcomprises multiple files or has multiple authors.Instead, imagine a system in which you could take a snapshot of stateof your files and also provide a description outlining what changesyou have made. Now imagine that the system was able to store and keeptrack of a succession of such versions in such a way that allows youto roll back to any previous versions of the files and exchange theentire history of changes with others collaborators - that is thepurpose of git.```{tikz}%| label: Fig11%| engine: tikz%| echo: false%| cache: true%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}]\tikzstyle{fileText} = [font={\fontspec[Scale=1]{InconsolataSemiCondensed-Regular}}] \definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_index}{rgb}{0.78,0.86,0.27}\definecolor{color_local}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\definecolor{color_master}{rgb}{0.36,0.27,0.87}\definecolor{color_head}{HTML}{6495ED} %\definecolor{color_head}{rgb}{0.26,0.65,0.91}\newcommand{\file}[3] {\coordinate (#3) at (#1);\draw [anchor=top left](#1) rectangle ++(1.5,-2);\draw [fill=color_workspace]($(#1) +(1.5,0)$) -- ++(0,-0.5) --++(-0.5,0.5) -- cycle; \node [anchor=north west,fileText] at ($(#1) +(0,-0.5)$) {#2};}\begin{tikzpicture}%Version 1\file{0,0}{\begin{minipage}{1.3cm}\textcolor{red}{- item 1}\\\textcolor{red}{- item 2}\end{minipage}}{F1}\node [fileText] at ($(F1) + (0.75,-2.3)$) {todo.txt};\file{0,-3}{\begin{minipage}{1.3cm}\textcolor{red}{Title}\\[1em]\textcolor{red}{content}\end{minipage}}{F1a}\node [fileText] at ($(F1a) + (0.75,-2.3)$) {fileA.doc};\node [fileText] at ($(F1) + (0.2,0.5)$) (V1) {Version 1};\draw (V1.200) -- (V1.200 |- F1a);\draw (V1.200 |- F1) -- (F1);\draw (V1.200 |- F1a) -- (F1a);%Version 2\file{3,0}{\begin{minipage}{1.3cm}- item 1\\- item 2\\\textcolor{red}{- item 3}\end{minipage}}{F2}\node [fileText] at ($(F2) + (0.75,-2.3)$) {todo.txt};\file{3,-3}{\begin{minipage}{1.3cm}\textcolor{black}{Title}\\[1em]\textcolor{black}{content}\end{minipage}}{F2a}\node [fileText] at ($(F2a) + (0.75,-2.3)$) {fileA.doc};\file{3,-6}{\begin{minipage}{1.3cm}\textcolor{red}{ID,Num}\\\textcolor{red}{1,10}\\\textcolor{red}{2,15}\end{minipage}}{F2b}\node [fileText] at ($(F2b) + (0.75,-2.3)$) {fileB.csv};\node [fileText] at ($(F2) + (0.2,0.5)$) (V2) {Version 2};\draw (V2.200) -- (V2.200 |- F2b);\draw (V2.200 |- F2) -- (F2);\draw (V2.200 |- F2a) -- (F2a);\draw (V2.200 |- F2b) -- (F2b);%Version 3\file{6,0}{\begin{minipage}{1.3cm}- item 1\\- item 2\\- item 3\\\textcolor{red}{- item 4}\end{minipage}}{F3}\node [fileText] at ($(F3) + (0.75,-2.3)$) {todo.txt};\file{6,-3}{\begin{minipage}{1.3cm}\textcolor{black}{Title}\\[1em]\textcolor{black}{content}\end{minipage}}{F3a}\node [fileText] at ($(F3a) + (0.75,-2.3)$) {fileA.doc};\file{6,-6}{\begin{minipage}{1.3cm}\textcolor{black}{ID,Num}\\\textcolor{black}{1,10}\\2,\textcolor{blue}{25}\end{minipage}}{F3b}\node [fileText] at ($(F3b) + (0.75,-2.3)$) {fileB.csv};\node [fileText] at ($(F3) + (0.2,0.5)$) (V3) {Version 3};\draw (V3.200) -- (V3.200 |- F3b);\draw (V3.200 |- F3) -- (F3);\draw (V3.200 |- F3a) -- (F3a);\draw (V3.200 |- F3b) -- (F3b);\node [fileText] at ($(F3) +(4,-1)$) {\begin{minipage}{3cm}An example of a more sophisticated versioning system\end{minipage}};\end{tikzpicture}```In the above diagram (which I must point out is **not actually howgit works**), you can see that we are keeping track of multipledocuments and potentially multiple changes within each document. Whatconstitutes a version (as in how many changes and to what files) iscompletely arbitrary. Each individual edit can define a separateversion.One of the issues with the above system is that there is a lot ofredundancy. With each new version an addition copy of the project'sentire filesystem (all its files) must be stored. In the above case,Version 2 and 3 both contain identical copies of`fileA.doc`. Is there a way of reducing the required sizeof the snapshots by only keeping copies of those that have actuallychanged? **this is what git achieves**. Git versions (or snapshotsknown as _commits_) store files that have changed since theprevious and files that have not changed are only represented by linksto instances of these files within previous snapshots.```{tikz}%| label: Fig12%| engine: tikz%| echo: false%| cache: true%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}]\tikzstyle{fileText} = [font={\fontspec[Scale=1]{InconsolataSemiCondensed-Regular}}] \definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_index}{rgb}{0.78,0.86,0.27}\definecolor{color_local}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\definecolor{color_master}{rgb}{0.36,0.27,0.87}\definecolor{color_head}{HTML}{6495ED} %\definecolor{color_head}{rgb}{0.26,0.65,0.91}\newcommand{\file}[3] {\coordinate (#3) at (#1);\draw [anchor=top left](#1) rectangle ++(1.5,-2);\draw [fill=color_workspace]($(#1) +(1.5,0)$) -- ++(0,-0.5) --++(-0.5,0.5) -- cycle; \node [anchor=north west,fileText] at ($(#1) +(0,-0.5)$) {#2};}\begin{tikzpicture}%Version 1\file{0,0}{\begin{minipage}{1.3cm}\textcolor{red}{- item 1}\\\textcolor{red}{- item 2}\end{minipage}}{F1}\node [fileText] at ($(F1) + (0.75,-2.3)$) {todo.txt};\file{0,-3}{\begin{minipage}{1.3cm}\textcolor{red}{Title}\\[1em]\textcolor{red}{content}\end{minipage}}{F1a}\node [fileText] at ($(F1a) + (0.75,-2.3)$) {fileA.doc};\node [fileText] at ($(F1) + (0.2,0.5)$) (V1) {Version 1};\draw (V1.200) -- (V1.200 |- F1a);\draw (V1.200 |- F1) -- (F1);\draw (V1.200 |- F1a) -- (F1a);%Version 2\file{3,0}{\begin{minipage}{1.3cm}- item 1\\- item 2\\\textcolor{red}{- item 3}\end{minipage}}{F2}\node [fileText] at ($(F2) + (0.75,-2.3)$) {todo.txt};\file{3,-3}{\begin{minipage}{1.3cm}\textcolor{black}{Title}\\[1em]\textcolor{black}{content}\end{minipage}}{F2a}\node [fileText] at ($(F2a) + (0.75,-2.3)$) {fileA.doc};\file{3,-6}{\begin{minipage}{1.3cm}\textcolor{red}{ID,Num}\\\textcolor{red}{1,10}\\\textcolor{red}{2,15}\end{minipage}}{F2b}\node [fileText] at ($(F2b) + (0.75,-2.3)$) {fileB.csv};\node [fileText] at ($(F2) + (0.2,0.5)$) (V2) {Version 2};\draw (V2.200) -- (V2.200 |- F2b);\draw (V2.200 |- F2) -- (F2);\draw (V2.200 |- F2a) -- (F2a);\draw (V2.200 |- F2b) -- (F2b);%Version 3\file{6,0}{\begin{minipage}{1.3cm}- item 1\\- item 2\\- item 3\\\textcolor{red}{- item 4}\end{minipage}}{F3}\node [fileText] at ($(F3) + (0.75,-2.3)$) {todo.txt};%\file{6,-3}{\begin{minipage}{1.3cm}\textcolor{black}{Title}\\[1em]\textcolor{black}{content}\end{minipage}}{F3a}\node [anchor=west,text=green!50!black!50] at (6,-3) (F3a) {\#\#\#\#\#\#}; \node [fileText] at ($(F3 |- F3a) + (0.75,-0.5)$) {fileA.doc};\draw[->,very thick,color=green!50!black!50] (F3a) -- ($(F2a) +(1.6,-0.5)$);\file{6,-6}{\begin{minipage}{1.3cm}\textcolor{black}{ID,Num}\\\textcolor{black}{1,10}\\2,\textcolor{blue}{25}\end{minipage}}{F3b}\node [fileText] at ($(F3b) + (0.75,-2.3)$) {fileB.csv};\node [fileText] at ($(F3) + (0.2,0.5)$) (V3) {Version 3};\draw (V3.200) -- (V3.200 |- F3b);\draw (V3.200 |- F3) -- (F3);\draw (V3.200 |- F3a) -- (F3a);\draw (V3.200 |- F3b) -- (F3b);\node [fileText] at ($(F3) +(4,-1)$) {\begin{minipage}{3cm}An example of a more sophisticated, yet efficient versioning system\end{minipage}};\end{tikzpicture}```Now consider the following:- You might have noticed that a new version can comprise multiple changes across multiple files. However, what if we have made numerous changes to numerous files over the course of an editing session (perhaps simultaneously addressing multiple different editing suggestions at a time), yet we did not want to lump all of these changes together into a single save point (snapshot). For example, the multiple changes might constitute addressing three independent issues, so although all edits were made simultaneously, we wish to record and describe the changes in three separate snapshots. - What if this project had multiple contributors some of whom are working on new components of the project and some whom are working simultaneously on the same set of files? How can the system ensure that all contributors are in sync with each other and that new components are only introduced to the project proper once they are stable and agreed upon?- What if there are files present within our project that we do not wish to keep track of. These files could be log files, compilation intermediates etc.- Given that projects can comprise many files (some of which can be large), is it possible to store compressed files so as to reduce the storage and bandwidth burden?# Overview of gitThe above discussion provides context for understanding how git works.Within git, files can exist in one of four states:- **untracked** - these are files within the directory tree that are not to be included in the repository (not part of any snapshot)- **modified** - these are files that have changed since the last snapshot- **staged** - these are files that are nominated to be part of the next snapshot- **committed** - these are files that are represented in a stored snapshot (called a _commit_). One a snapshot is committed, it is a permanent part of the repositories historySince untracked files are not part of a repository, we will ignorethese for now.Conceptually, there are three main sections of a repository:- **Working directory** - (or **Workspace**) is the obvious tree (set of files and folders) that is present on disc and comprises the actual files that you directly create, edit etc.- **Staging area** - (or **index**) is a hidden file that contains metadata about the files to be included in the next snapshot (commit)- **Repository** - the snapshots (commits). The commits are themselves just additional metadata pointing to a particular snapshot.A superficial representation of some aspects of the git versioncontrol system follows. Here, the physical file tree in the_workspace_ can be added to the _staging area_ before this snapshotcan be committed to the _local repository_.After we add the two files (`file 1` and `file 2`), both files will beconsidered in an _untracked_ state. Adding the files to the _stagingarea_ changes their state to _staged_. Finally when we commit, thefiles are in a _committed_ state.```{tikz}%| label: Fig13%| engine: tikz%| echo: false%| cache: true%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \usetikzlibrary{arrows.meta}\tikzstyle{TARGET} = [font={\fontspec[Scale=2]{NotoSans-Regular}}] \tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}]\tikzstyle{TREE} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}] \tikzstyle{fileText} = [font={\fontspec[Scale=1]{InconsolataSemiCondensed-Regular}}] \definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_index}{rgb}{0.78,0.86,0.27}\definecolor{color_local}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\definecolor{color_master}{rgb}{0.36,0.27,0.87}\definecolor{color_head}{HTML}{6495ED} %\definecolor{color_head}{rgb}{0.26,0.65,0.91}% A template for making the storage symbol\newcommand{\state}[3]{\draw (#1) node [draw=none,fill=#2,shape=circle,minimum width=2cm] (#3) {\begin{tikzpicture}\node [draw=white, fill=white,shape=cylinder,shape aspect=1.3 ,shape border rotate=90,minimum height=1.6cm,minimum width=1.5cm] at (0,0) (Cylinder) {};\draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.1cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.5cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.9cm)$) arc (-180:0:0.8cm and 0.2cm); \end{tikzpicture}};}% Define dirtree\makeatletter\newcount\dirtree@lvl\newcount\dirtree@plvl\newcount\dirtree@clvl\def\dirtree@growth{%\ifnum\tikznumberofcurrentchild=1\relax\global\advance\dirtree@plvl by 1\expandafter\xdef\csname dirtree@p@\the\dirtree@plvl\endcsname{\the\dirtree@lvl}\fi\global\advance\dirtree@lvl by 1\relax\dirtree@clvl=\dirtree@lvl\advance\dirtree@clvl by -\csname dirtree@p@\the\dirtree@plvl\endcsname\pgf@xa=0.25cm\relax\pgf@ya=-0.5cm\relax\pgf@ya=\dirtree@clvl\pgf@ya\pgftransformshift{\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%\ifnum\tikznumberofcurrentchild=\tikznumberofchildren\global\advance\dirtree@plvl by -1\fi}\tikzset{dirtree/.style={growth function=\dirtree@growth,every node/.style={anchor=north},every child node/.style={anchor=west},edge from parent path={(\tikzparentnode\tikzparentanchor) |- (\tikzchildnode\tikzchildanchor)}}}\makeatother\newcommand{\file}[3] {\coordinate (#3) at (#1);\draw [anchor=top left](#1) rectangle ++(1.5,-2);\draw [fill=color_workspace]($(#1) +(1.5,0)$) -- ++(0,-0.5) --++(-0.5,0.5) -- cycle; \node [anchor=north west,fileText] at ($(#1) +(0,-0.5)$) {#2};}\begin{tikzpicture}\state{0,0}{color_workspace}{W}\node[TARGET,fill=white] at ($(W.north) +(0,0.5cm)$) {Workspace};\state{6,0}{color_index}{I}\node[TARGET,fill=white] at ($(I.north) +(0,0.5cm)$) {Staging Area};\state{12,0}{color_local}{L}\node[TARGET,fill=white] at ($(L.north) +(0,0.5cm)$) {Local Repository};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_index] ($(W.east) + (0.1cm,0)$) -- ($(I.west) + (-0.1cm,0)$) node[anchor=center,pos=0.5, text=black,align=center,CODE] {add};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_local] ($(I.east) + (0.1cm,0)$) -- ($(L.west) + (-0.1cm,0)$) node[anchor=center,pos=0.5, text=black,align=center,CODE] {commit};\node [anchor=north west] at ($(W) +(-1cm,-1cm)$) (W0) {\begin{tikzpicture}[dirtree,TREE]\node {} child {node {file 2}}child { node {file 1}};\end{tikzpicture}};%\draw[decorate,decoration={brace,amplitude=5pt},very thick] ($(W0.north) + (-1cm,-1cm)$) -- ($(W0.south) + (-1cm,0.2cm)$);\node [anchor=north west] at ($(I) +(-1cm,-1cm)$) (W0) {\begin{tikzpicture}[dirtree,TREE,text=gray,color=gray]\node {} child {node {file 2}}child { node {file 1}};\end{tikzpicture}};\node [anchor=north west] at ($(L) +(-1cm,-1cm)$) (L0) {\begin{tikzpicture}[dirtree,TREE]\node {} child {node {file 2}}child { node {file 1}};\end{tikzpicture}};\draw[decorate,decoration={brace,amplitude=5pt},very thick] ($(L0.north) + (1.3cm,-0.4cm)$) -- ($(L0.south) + (1.3cm,0.2cm)$);\node [TREE,anchor=west] at ($(L0) +(1.5,-0.1)$) {committed snapshot 1}; \end{tikzpicture}```Now if we add another file (`file 3`) to our_workspace_, add this file to the _staging area_ and thencommit the change, the resulting committed snapshot in the _localrepository_ will resemble the _workspace_. Note, although the_staging area_ contains all three files, only `file 3`points to any new internal content - since `file 1` and`file 2` have unmodified, their instances in the _stagingarea_ point to the same instances as previous. Similarly, thesecond commit in the _Local repository_ will point to one newrepresentation (associated with `file 3`) and two previousrepresentations (associated with `file 1` and `file2`).```{tikz}%| label: Fig14%| engine: tikz%| echo: false%| cache: true%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \usetikzlibrary{arrows.meta}\tikzstyle{TARGET} = [font={\fontspec[Scale=2]{NotoSans-Regular}}] \tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}]\tikzstyle{TREE} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}] \tikzstyle{fileText} = [font={\fontspec[Scale=1]{InconsolataSemiCondensed-Regular}}] \definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_index}{rgb}{0.78,0.86,0.27}\definecolor{color_local}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\definecolor{color_master}{rgb}{0.36,0.27,0.87}\definecolor{color_head}{HTML}{6495ED} %\definecolor{color_head}{rgb}{0.26,0.65,0.91}% A template for making the storage symbol\newcommand{\state}[3]{\draw (#1) node [draw=none,fill=#2,shape=circle,minimum width=2cm] (#3) {\begin{tikzpicture}\node [draw=white, fill=white,shape=cylinder,shape aspect=1.3 ,shape border rotate=90,minimum height=1.6cm,minimum width=1.5cm] at (0,0) (Cylinder) {}; \draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.1cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.5cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.9cm)$) arc (-180:0:0.8cm and 0.2cm); \end{tikzpicture}};}% Define dirtree\makeatletter\newcount\dirtree@lvl\newcount\dirtree@plvl\newcount\dirtree@clvl\def\dirtree@growth{%\ifnum\tikznumberofcurrentchild=1\relax\global\advance\dirtree@plvl by 1\expandafter\xdef\csname dirtree@p@\the\dirtree@plvl\endcsname{\the\dirtree@lvl}\fi\global\advance\dirtree@lvl by 1\relax\dirtree@clvl=\dirtree@lvl\advance\dirtree@clvl by -\csname dirtree@p@\the\dirtree@plvl\endcsname\pgf@xa=0.25cm\relax\pgf@ya=-0.5cm\relax\pgf@ya=\dirtree@clvl\pgf@ya\pgftransformshift{\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%\ifnum\tikznumberofcurrentchild=\tikznumberofchildren\global\advance\dirtree@plvl by -1\fi}\tikzset{dirtree/.style={growth function=\dirtree@growth,every node/.style={anchor=north},every child node/.style={anchor=west},edge from parent path={(\tikzparentnode\tikzparentanchor) |- (\tikzchildnode\tikzchildanchor)}}}\makeatother\newcommand{\file}[3] {\coordinate (#3) at (#1);\draw [anchor=top left](#1) rectangle ++(1.5,-2);\draw [fill=color_workspace]($(#1) +(1.5,0)$) -- ++(0,-0.5) --++(-0.5,0.5) -- cycle; \node [anchor=north west,fileText] at ($(#1) +(0,-0.5)$) {#2};}\begin{tikzpicture}\state{0,0}{color_workspace}{W}\node[TARGET,fill=white] at ($(W.north) +(0,0.5cm)$) {Workspace};\state{6,0}{color_index}{I}\node[TARGET,fill=white] at ($(I.north) +(0,0.5cm)$) {Staging Area};\state{12,0}{color_local}{L}\node[TARGET,fill=white] at ($(L.north) +(0,0.5cm)$) {Local Repository};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_index] ($(W.east) + (0.1cm,0)$) -- ($(I.west) + (-0.1cm,0)$) node[anchor=center,pos=0.5, text=black,align=center,CODE] {add};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_local] ($(I.east) + (0.1cm,0)$) -- ($(L.west) + (-0.1cm,0)$) node[anchor=center,pos=0.5, text=black,align=center,CODE] {commit};\node [anchor=north west] at ($(W) +(-1cm,-1cm)$) (W0) {\begin{tikzpicture}[dirtree,TREE]\node {} child { node {file 3}}child [text=color_workspace]{node {file 2}}child [text=color_workspace]{ node {file 1}};\end{tikzpicture}};%\draw[decorate,decoration={brace,amplitude=5pt},very thick] ($(W0.north) + (-1cm,-1cm)$) -- ($(W0.south) + (-1cm,0.2cm)$);\node [anchor=north west] at ($(I) +(-1cm,-1cm)$) (W0) {\begin{tikzpicture}[dirtree,TREE,color=gray,text=gray]\node {} child { node {file 3}}child {node {file 2}}child {node {file 1}};\end{tikzpicture}};\node [anchor=north west] at ($(L) +(-1cm,-1cm)$) (L0) {\begin{tikzpicture}[dirtree,TREE]\node {} child { node {file 3}}child {node {file 2}}child { node {file 1}};\end{tikzpicture}};\draw[decorate,decoration={brace,amplitude=5pt},very thick] ($(L0.north) + (1.3cm,-0.4cm)$) -- ($(L0.south) + (1.3cm,0.2cm)$);\node [TREE,anchor=west] at ($(L0) +(1.5,-0.1)$) {committed snapshot 2}; \node [anchor=north west] at ($(L0) +(-1.2cm,-1cm)$) (L1) {\begin{tikzpicture}[dirtree,TREE]\node {} child {node {file 2}}child { node {file 1}};\end{tikzpicture}};\draw[decorate,decoration={brace,amplitude=5pt},very thick] ($(L1.north) + (1.3cm,-0.4cm)$) -- ($(L1.south) + (1.3cm,0.2cm)$);\node [TREE,anchor=west] at ($(L1) +(1.5,-0.1)$) {committed snapshot 1}; \end{tikzpicture}```Initially, it might seem that there is an awful lot of duplicationgoing on. For example, if we make a minor alteration to a file, whynot just commit the change (delta) instead of an entirely new copy?Well, periodically, git will perform **garbage collection** on therepository. This process **repacks** the objects together into asingle object that comprises only the original blobs and theirsubsequent deltas - thereby gaining efficiency. The process of garbagecollection can also be forced at any time via:```{bash}#| label: gitgc#| echo: true#| eval: false#| classes: bash#| highlight-style: zenburngit gc```During the evolution of most projects, situations arise in which wewish to start work on new components or features that might representa substantial deviation from the main line of evolution. Often, wewould very much like to be able to quarantine the main thread of theproject from these new developments. For example, we may wish to beable to continue tweaking the main project files (in order to addressminor issues and bugs), while at the same time, performing major editsthat take the project in a different direction.This is called _branching_. The main evolutionary thread of theproject is referred to as the **main** _branch_. Deviationsfrom the _main branch_ are generally called **branches** andcan be given any name (other than 'main' or 'HEAD'). For example, wecould start a new _branch_ called 'Feature' where we can evolvethe project in one direction whilst still being able to activelydevelop the _main branch_ at the same time. 'Feature' and'main' _branches_ are depicted in the left hand sequence ofcircles of the schematic below.```{tikz}%| label: Fig-overview%| engine: tikz%| echo: false%| cache: true%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\tikzstyle{TARGET} = [font={\fontspec[Scale=2]{NotoSans-Regular}}]\tikzstyle{refText} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}]\begin{tikzpicture}\commit{}{A}{color_inactive}{}{}\commit{right = 1cm of A}{B}{color_inactive}{}{}\commit{right = 1cm of B}{C}{color_commit}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\draw [-,line width=3pt,draw=black!60] (B) -- (A);\draw [-,line width=3pt,draw=black!60] (C) -- (B);\commit{above = 0.5cm of B}{D}{color_inactive}{}{}\draw [-,line width=3pt,draw=black!60] (A.east) to[out=0,in=180] (D);\master{right = 0.5cm of C}\draw[->,line width=3pt,draw=black!60] (master) -- (C);\HEAD{right = 0.5cm of master}\draw[->,line width=3pt,draw=black!60] (HEAD) -- (master);\branch{right = 0.5cm of D}{Feature}\draw[->,line width=3pt,draw=black!60] (Feature) -- (D);\rcommit{right = 3cm of HEAD}{rA}{color_inactive}{}{}\rcommit{right = 1cm of rA}{rB}{color_inactive}{}{}\rcommit{right = 1cm of rB}{rC}{color_inactive}{}{}\rcommit{right = 1cm of rC}{rC2}{color_commit}{}{}\draw [-,line width=3pt,draw=black!60] (rA) -- ++(-1,0);\draw [-,line width=3pt,draw=black!60] (rB) -- (rA);\draw [-,line width=3pt,draw=black!60] (rC) -- (rB);\draw [-,line width=3pt,draw=black!60] (rC2) -- (rC);\rcommit{above = 0.5cm of rB}{rD}{color_inactive}{}{}\draw [-,line width=3pt,draw=black!60] (rA.east) to[out=0,in=180] (rD);\rmaster{right = 0.5cm of rC2}\draw[->,line width=3pt,draw=black!60] (rmaster) -- (rC2);\branch{right = 0.5cm of rD}{origin/Feature}\draw[->,line width=3pt,draw=black!60] (origin/Feature) -- (rD);\rcommit{below = 0.5cm of rB}{rE}{color_inactive}{}{}\draw [-,line width=3pt,draw=black!60] (rA.east) to[out=0,in=180] (rE);\branch{right = 0.5cm of rE}{origin/dev}\draw[->,line width=3pt,draw=black!60] (origin/dev) -- (rE);\rHEAD{right = 0.5cm of rmaster}\draw[->,line width=3pt,draw=black!60] (rHEAD) -- (rmaster);\state{$(A) +(0,3cm)$}{color_local}{L}\node[TARGET,fill=white] at ($(L.north) +(0,0.5cm)$) {Local Repository};\state{$(rA) +(0,3cm)$}{color_remote}{R}\node[TARGET,fill=white] at ($(R.north) +(0,0.5cm)$) {Remote Repository};\end{tikzpicture}```The circles represent **commits** (stored snapshots). We can see thatthe first commit is the common ancestor of the 'Feature' and 'main'_branch_. **HEAD** is a special reference that points to the _tip_ ofthe currently active _commit_. It indicates where the next _commit_will be built onto. In diagram above, `HEAD` is pointing to the last_commit_ in `main`. Hence the next _commit_ will build on this_commit_. To develop the `Feature` _branch_ further, we first have tomove `HEAD` to the tip of the `Feature` _branch_.We can later `merge` the `Feature` _branch_ into the `main` _branch_in order to make the new changes mainstream.To support collaboration, there can also be a _remote repository_(referred to as **origin** and depicted by the squares in the figureabove). Unlike a _local repository_, a _remote repository_ does notcontain a _workspace_ as files are not directly edited in the _remoterepository_. Instead, the _remote repository_ acts as a permanentlyavailable conduit between multiple contributors.In the diagram above, we can see that the _remote repository_(`origin`) has an additional _branch_ (in this called `dev`). Thecollaborator whose _local repository_ is depicted above has either notyet obtained (**pulled**) this _branch_ or has elected not to (asperhaps it is not a direction that they are involved in).We also see that the `main` _branch_ on the _remote repository_ has anewer (additional) _commit_ than the _local repository_.Prior to working on _branch_ a collaborator should first get anyupdates to the _remote repository_. This is a two step process.Firstly, the collaborator **fetches** any changes and then secondly**merges** those changes into their version of the _branch_.Collectively, these two actions are called a **pull**.To make local changes available to others, the collaborator can**push** _commits_ up to the _remote repository_. The _pushed_ changesare applied directly to the nominated _branch_ so it is the usersresponsibility to ensure as much as possible, their local repositoryalready included the most recent _remote repository_ changes (byalways _pulling_ before _pushing_).```{tikz}%| label: Fig-git%| engine: tikz%| echo: false%| cache: true%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\usetikzlibrary{arrows.meta}\tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}] \tikzstyle{TARGET} = [font={\fontspec[Scale=2]{NotoSans-Regular}}]\tikzstyle{refText} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}]\begin{tikzpicture}\coordinate (R1) at (0,8cm);\coordinate (R2) at (0,6cm);\coordinate (R3) at (0,4cm);\coordinate (R4) at (0,2cm);\coordinate (R5) at (0,0cm);\coordinate (R6) at (0,-3cm);\coordinate (R7) at (0,-6cm);\coordinate (R8) at (0,-6cm);\coordinate (R8) at (0,-8cm);\coordinate (R9) at (0,-10cm);\coordinate (R10) at (0,-12cm);\coordinate (R11) at (0,-14cm);\coordinate (C1) at (0cm,-2cm);\coordinate (C2) at (8cm,-2cm);\coordinate (C3) at (16cm,-2cm);\coordinate (C4) at (24cm,-2cm);\definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_index}{rgb}{0.78,0.86,0.27}\definecolor{color_local}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\draw [line width=0.1cm,draw=color_workspace] (C1.west |- R1.north) -- (C1.west |- R11.south); \node [draw=none,fill=color_workspace,shape=circle,minimum width=2cm] at (C1) (W) {\begin{tikzpicture}\node [draw=white, fill=white,shape=cylinder,shape aspect=1.3 ,shape border rotate=90,minimum height=1.6cm,minimum width=1.5cm] at (0,0) (Cylinder) {};\draw[draw=color_workspace,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.1cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=color_workspace,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.5cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=color_workspace,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.9cm)$) arc (-180:0:0.8cm and 0.2cm); \end{tikzpicture} };\node[TARGET,fill=white] at ($(W.south) +(0,-0.5cm)$) {Workspace};\draw [line width=0.1cm,draw=color_index] (C2.west |- R1.north) -- (C2.west |- R11.south); \node [draw=none,fill=color_index,shape=circle,minimum width=2cm] at (C2) (I) {\begin{tikzpicture}\node [draw=white, fill=white,shape=cylinder,shape aspect=1.3 ,shape border rotate=90,minimum height=1.6cm,minimum width=1.5cm] at (0,0) (Cylinder) {};\draw[draw=color_index,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.1cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=color_index,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.5cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=color_index,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.9cm)$) arc (-180:0:0.8cm and 0.2cm); \end{tikzpicture} };\node[TARGET,fill=white] at ($(I.south) +(0,-0.5cm)$) {Staging area (Index)};\draw [line width=0.1cm,draw=color_local] (C3.west |- R1.north) -- (C3.west |- R11.south);\node [draw=none,fill=color_local,shape=circle,minimum width=2cm] at (C3) (L) {\begin{tikzpicture}\node [draw=white, fill=white,shape=cylinder,shape aspect=1.3 ,shape border rotate=90,minimum height=1.6cm,minimum width=1.5cm] at (0,0) (Cylinder) {};\draw[draw=color_local,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.1cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=color_local,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.5cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=color_local,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.9cm)$) arc (-180:0:0.8cm and 0.2cm); \end{tikzpicture} };\node[TARGET,fill=white] at ($(L.south) +(0,-0.5cm)$) {Local Repository};\draw [line width=0.1cm,draw=color_remote] (C4.west |- R1.north) -- (C4.west |- R11.south);\node [draw=none,fill=color_remote,shape=circle,minimum width=2cm] at (C4) (R) {\begin{tikzpicture}\node [draw=white, fill=white,shape=cylinder,shape aspect=1.3 ,shape border rotate=90,minimum height=1.6cm,minimum width=1.5cm] at (0,0) (Cylinder) {};\draw[draw=color_remote,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.1cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=color_remote,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.5cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=color_remote,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.9cm)$) arc (-180:0:0.8cm and 0.2cm); \end{tikzpicture} };\node[TARGET,fill=white] at ($(R.south) +(0,-0.5cm)$) {Remote Repository};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_index] ($(C1.west |- R2.south) + (0.1cm,0)$) -- ($(C2.west |- R2.south) + (-0.1cm,0)$) node[anchor=west,pos=0, text=black,align=center,CODE] {git rm / git mv};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_index] ($(C1.west |- R3.south) + (0.1cm,0)$) -- ($(C2.west |- R3.south) + (-0.1cm,0)$) node[anchor=west,pos=0, text=black,align=center,CODE] {git add};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_local] ($(C2.west |- R3.south) + (0.1cm,0)$) -- ($(C3.west |- R3.south) + (-0.1cm,0)$) node[anchor=west,pos=0, text=black,align=center,CODE] {git commit};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_remote] ($(C3.west |- R3.south) + (0.1cm,0)$) -- ($(C4.west |- R3.south) + (-0.1cm,0)$) node[anchor=west,pos=0, text=black,align=center,CODE] {git push};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_remote] ($(C4.west |- R7.south) + (-0.1cm,0)$) -- ($(C1.west |- R7.south) + (0.1cm,0)$) node[anchor=east,pos=0, text=black,CODE] {git pull / git reset --hard <remote/branch>};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_remote] ($(C4.west |- R8.south) + (-0.1cm,0)$) -- ($(C3.west |- R8.south) + (0.1cm,0)$) node[anchor=east,pos=0, text=black,CODE] {git fetch};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_local] ($(C3.west |- R8.south) + (-0.1cm,0)$) -- ($(C1.west |- R8.south) + (0.1cm,0)$) node[anchor=east,pos=0, text=black,CODE] {git merge / git rebase};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_workspace] ($(C3.west |- R9.south) + (-0.1cm,0)$) -- ($(C1.west |- R9.south) + (0.1cm,0)$) node[anchor=east,pos=0, text=black,CODE] {git checkout HEAD / git reset --hard};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_index] ($(C3.west |- R10.south) + (-0.1cm,0)$) -- ($(C2.west |- R10.south) + (0.1cm,0)$) node[anchor=east,pos=0, text=black,CODE] {git reset --soft};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_workspace] ($(C2.west |- R10.south) + (-0.1cm,0)$) -- ($(C1.west |- R10.south) + (0.1cm,0)$) node[anchor=east,pos=0, text=black,CODE] {git checkout};\draw[Triangle Cap-Triangle Cap,very thick, line width=1cm, draw=color_index!40] ($(C2.west |- R4.south) + (-0.1cm,0)$) -- ($(C1.west |- R4.south) + (0.1cm,0)$) node[anchor=center,pos=0.5, text=black,CODE] {git diff};\draw[Triangle Cap-Triangle Cap,very thick, line width=1cm, draw=color_local!40] ($(C3.west |- R5.south) + (-0.1cm,0)$) -- ($(C1.west |- R5.south) + (0.1cm,0)$) node[anchor=center,pos=0.5, text=black,CODE] {git diff HEAD};\node[rounded corners,CODE,fill=color_workspace,minimum height=1cm] at ($(R1) +(0,1.5cm)$) (init) {git init};\draw[line width=1cm,draw=color_workspace,-{Triangle Cap[cap angle=60,length=0.3cm]}] ($(init.south) +(0,-0.3cm)$) -- +(0,-0.5cm);\node[rounded corners,CODE,fill=color_remote,minimum height=1cm] at ($(C4 |- R1) +(0,1.5cm)$) (initbare) {git init --bare};\draw[line width=1cm,draw=color_remote,-{Triangle Cap[cap angle=60,length=0.3cm]}] ($(initbare.south) +(0,-0.3cm)$) -- +(0,-0.5cm);\end{tikzpicture}```# Installation::: panel-tabset ## WindowsGit Bash (Command Line Version):1. Download the Git for Windows installer from [Git for Windows](https://gitforwindows.org/) - Click the Download button - Select the latest version from the list of `Assets`2. Run the installer and follow the installation prompts.3. Choose the default options unless you have specific preferences.4. Select the default text editor (usually Vim) or choose another editor like Nano or Notepad++.5. Choose to use Git from the Windows Command Prompt (recommended).6. Complete the installation.## MacOSxUsing Homebrew:1. Open Terminal.2. Install Homebrew if not installed::::: {.indented}```{bash}#| label: install1#| echo: true#| eval: false#| cache: false#| engine: bash#| classes: bash/bin/bash-c"$(curl-fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"```:::3. Install Git using Homebrew::::: {.indented}```{bash}#| label: install2#| echo: true#| eval: false#| cache: false#| engine: bash#| classes: bashbrew install git```::::## Linux1. Open Terminal.:::: {.indented}Ubuntu/Debian:```{bash}#| label: install3a#| echo: true#| eval: false#| cache: false#| engine: bash#| classes: bashsudo apt updatesudo apt install git```:::::::: {.indented}Fedora:```{bash}#| label: install3b#| echo: true#| eval: false#| cache: false#| engine: bash#| classes: bashsudo dnf install git```:::::::: {.indented}Arch Linux:```{bash}#| label: install3c#| echo: true#| eval: false#| cache: false#| engine: bash#| classes: bashsudo pacman -S git```:::::::: {.indented}Linux (Red Hat/CentOS):```{bash}#| label: install3d#| echo: true#| eval: false#| cache: false#| engine: bash#| classes: bashsudo yum install git```:::::::To verify that the software is installed and accessible, open aterminal and issue the following:```{bash}#| label: install4a#| echo: true#| eval: true#| cache: false#| engine: bash#| classes: bashgit--version```::: {.callout-tip collapse="true"}## Unsure how to open a terminal?**Windows:**On Windows, you can access a terminal via one of the following: - via the command Prompt: - Press `Win + R` to open the Run dialog. - Type `cmd` and press `Enter`.- via PowerShell: - Press `Win + X` and select "Windows PowerShell."- Git Bash (Optional): - if Git is installed (which we are hoping it is!), open "Git Bash" for a Unix-like terminal experience.**MacOS:**- via Terminal: - Press `Cmd + Space` to open Spotlight. - Type `terminal` and press `Enter`.**Linux:**Oh please. You cannot seriously tell me that you are using Linux anddon't know how to access a terminal.:::In the command above, pay particular attention to the number ofhyphens in the above command - there are two in a row and no spacesbetween the `--` and the word `version`.If you get output similar to above (an indication of what version ofgit you have on your system), then it is likely to be properlyinstalled. If instead you get an error message, then it is likely thatgit is not properly installed and you should try again.# Getting startedBefore using git, it is a good idea to define some global (applied toall your gits) settings. These include your name and email address andwhilst not essential, they are applied to all actions you perform sothe it is easier for others to track the route of changes etc.```{bash}#| label: git-config#| echo: true#| eval: false#| cache: false#| classes: bash#| engine: bashgit config --global user.name "Your Name"git config --global user.email "your_email@whatever.com"```::: {.callout-note}In the above, you should replace "Your Name" with your actual name.This need not be a username (or even a real name) it is not crossreferenced anywhere. It is simply to use in collaboration so that yourcollaborators know who is responsible for your commits.Similarly, you should replace "your_email@whatever.com" with an emailthat you are likely to monitor. This need not be the same emailaddress you have used to register a Github account etc, it is just sothat collaborators have a way of contacting you.:::The remaining sections go through the major git versioning concepts.As previously indicated, git is a command driven program (technicallya family of programs). Nevertheless, many other applications (such asRStudio) are able to interface directly with git for some of the morecommonly used features. Hence, in addition to providing the commandline syntax for performing each task, where possible, this tutorialwill also provide instructions (with screen captures) for RStudio andemacs.# Setting up (initializing) a new repositoryFor the purpose of this tutorial, I will create a temporary folder the`tmp` _folder_ of my `home` _directory_ into which to create andmanipulate repositories. To follow along with this tutorial, you areencouraged to do similarly.## Initialize local repository```{tikz}%| label: Fig1a%| engine: tikz%| echo: false%| cache: true%| dependson: common%| include: false%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\tikzstyle{refText} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}]\begin{tikzpicture}\commit{}{A}{white}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\master{right = 0.5cm of A}\draw[->,line width=3pt,draw=black!60] (master) -- (A);\HEAD{right = 0.5cm of master}\draw[->,line width=3pt,draw=black!60] (HEAD) -- (master);\end{tikzpicture} ``````{bash}#| label: Fig1a-conv#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Fig1a-1.pdf 10_git_files/figure-html/Fig1a-1.png ```![](10_git_files/figure-html/Fig1a-1.png)::: {.panel-tabset}## TerminalWe will start by creating a new directory (folder) which we will call`Repo1` in which to place our repository. All usual directory namingrules apply since it is just a regular directory.```{R}#| label: makedirectory1#| cache: false#| echo: falseif (!dir.exists("~/tmp")) dir.create("~/tmp")unlink('~/tmp/Repo1', recursive=TRUE, force=TRUE)``````{bash}#| label: makedirectory#| echo: true#| cache: false#| engine: bash#| classes: bashmkdir ~/tmp/Repo1```To create (or initialize) a new local repository, issue the `gitinit` _command_ in the root of the working directory youwish to contain the git repository. This can be either an emptydirectory or contain an existing directory/file structure. The`git init` _command_ will add a folder called`.git` to the directory. <b>This is a one timeoperation</b>.```{bash}#| label: git-init#| echo: true#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git init ```The `.git` _folder_ contains all the necessary_metadata_ to manage the repository.```{bash}#| label: git-initD#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1 ls-al``````{bash}#| label: git-initD2#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1 tree-a--charset unicode```config: this file stores settings such as the location of a remote repository that this repository is linked to.description: lists the name (and version) of a repositoryHEAD: lists a reference to the current checked out commit.hooks: a directory containing scripts that are executed at various stages (e.g. `pre-push.sample` is an example of a script executed prior to pushing)info: contains a file `exclude` that lists exclusions (files not to be tracked). This is like `.gitignore`, except is not versioned.objects: this directory contains SHA indexed files being trackedrefs: a master copy of all the repository refslogs: contains a history of each branch## RStudioThe repository that we are going to create in this demonstration couldbe considered to be a new standalone analysis. In Rstudio, this wouldbe considered a **project**. So, we will initialise the git repositorywhile we create a new Rstudio project. To do so:1. click on the `Project` selector in the top right of the Rstudio window (as highlighted by the red ellipse in the image below.![](../resources/rstudio_init1a.png){width=100%}2. select `New Project` from the dropdown menu3. select `New Directory` form the Create Project panel4. select `New Project` from the Project Type panel5. Provide a name for the new directory to be created and use the`Browse` button to locate a suitable position for this new directory. **Ensure that the `Create a git repository` checkbox is checked**![](../resources/rstudio_init2.png){width=50%}6. Click the `Create Project` buttonIf successful, you should notice a couple of changes - these arehighlighted in the following figure:![](../resources/rstudio_init3a.png){width=60%}- a new `Git` tab will appear in the top right panel- the contents of this newly created project/repository will appear in the `Files` tab of the bottom right panelIf the files and directories that begin with a `.` do not appear,click on the `More file commands` cog and make sure the `Show HiddenFiles` option is ticked.The newly created files/folders are:- `.git` - this directory houses the repository information and should not generally be edited directly- `.gitignore` - this file defines files/folders to be excluded from the repository. We will discuss this file more later- `.Rhistory` - this file will accrue a history of the commands you have evaluated in R within this project- `.Rproj.user` - this folder stores some project-specific temporary files- `Repo1.Rproj` - contains the project specific settings**Note that on the left side of the Rstudio window there are twopanels - one called "Console", the other called "Terminal". Theconsole window is for issuing R commands and the terminal window isfor issuing system (bash, shell) commands. Throughout this tutorial,as an alternative to using the point and click Rstudio methods, youcould instead issue the `Terminal` instructions into the "Terminal"panel. Indeed, there are some git commands that are not supporteddirectly by Rstudio and can only be entered into the terminal**## Emacs (magit):::Note, at this stage, no files are being tracked, that is, they are notpart of the repository.To assist in gaining a greater understanding of the workings of git,we will use a series of schematics diagrams representing the contentsof four important sections of the repository. Typically, these figureswill be contained within callout panels that expand/collapse uponclicking. However, for this first time, they will be standalone.In the first figure below, the left hand panel represents the contentsof the root directory (excluding the `.git` folder) - this is the_workspace_ and is currently empty.The three white panels represent three important parts of the innerstructure of the `.git` folder. A newly initialized repository isrelatively devoid of any specific metadata since there are no stagedor committed files. In the root of the `.git` folder, there is a filecalled `HEAD`.The figure is currently very sparse. However, as the repository grows,so the figure will become more complex.```{tikz}%| label: Fig-advanced1%| engine: tikz%| echo: false%| cache: true%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\usetikzlibrary{arrows.meta}\definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_tree}{HTML}{9ACD32} %\definecolor{color_tree}{rgb}{0.78,0.86,0.27}\definecolor{color_commit}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\definecolor{color_master}{rgb}{0.36,0.27,0.87}\definecolor{color_head}{HTML}{6495ED}\definecolor{color_index}{HTML}{E0FFFF} %\definecolor{color_index}{rgb}{1,1,1}\definecolor{color_file}{rgb}{0.8,0.8,0.8}\tikzstyle{TARGET} = [font={\fontspec[Scale=2]{NotoSans-Regular}}] \tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}] \tikzstyle{TREE} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}] \tikzstyle{fileText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] %Define a file \newcommand{\file}[4] {\def\corner{0.15in};\def\cornerradius{0.02in};\def\lwidth{0.02in};\def\h{0.5in};\def\w{0.85in};\def\nline{0};\def\iconmargin{0.1in};\def\topmargin{0.3in};\node at (#1) {\begin{tikzpicture}\coordinate (nw) at ($(-0.05in*1,-0.15in*1)$);\coordinate (#3) at (#1); \coordinate (ne0) at ($(nw) + (\w, 0)$);\coordinate (ne1) at ($(ne0) - (\corner, 0)$);\coordinate (ne2) at ($(ne0) - (0, \corner)$);\coordinate (se) at ($(ne0) + (0, -\h)$); \filldraw [-, line width = \lwidth, fill=#4] (nw) -- (ne1) -- (ne2)[rounded corners=\cornerradius]--(se) -- (nw|-se) -- cycle;\draw [-, line width = \lwidth] (ne1) [rounded corners=\cornerradius]-- (ne1|-ne2) -- (ne2);\node [anchor=north west,TREE] at (nw) {#2};\foreach \k in {0,...,\nline}{\draw [-, line width = \lwidth, line cap=round] ($(nw|-se) + (\iconmargin,\iconmargin) + (0,{(\k-1)/(\nline-1)*(\h - \iconmargin - \topmargin)})$)-- ++ ($(\w,0) - 2*(\iconmargin,0)$);}\end{tikzpicture}};}% end of file definition\begin{tikzpicture}\coordinate (G_ul) at (0,0);\coordinate (G_ll) at ($(G_ul) +(0,-10)$);\coordinate (G_lr) at ($(G_ll) +(4,0)$);\coordinate (G_ur) at ($(G_ul) +(4,0)$);\node[TREE,anchor=west] at ($(G_ul) +(0,-0.5)$) (git) {.git/};\file{$(git.west) +(1.5,-2)$}{HEAD}{HEAD}{color_head}\draw (G_ul) -- (G_ll) -- (G_lr) -- (G_ur) -- cycle;%refs\node[TREE,anchor=west] at ($(G_ul) +(-5,-0.5)$) (git_ref) {.git/refs/};\draw[] ($(G_ul) +(-5,0)$) -- ++(0,-10) -- ++(4.9,0) -- ++(0,10) -- cycle;%objects\node[TREE,anchor=west] at ($(G_ul) +(-17,-0.5)$) (git_object) {.git/objects/};\draw[] ($(G_ul) +(-17,0)$) -- ++(0,-10) -- ++(11.9,0) -- ++(0,10) -- cycle;%files\node[TREE,anchor=west] at ($(G_ul) +(-21,-0.5)$) (root) {/};\begin{pgfonlayer}{background}\draw[fill=color_workspace!20] ($(G_ul) +(-21,0)$) -- ++(0,-10) -- ++(3.9,0) -- ++(0,10) -- cycle;\end{pgfonlayer}\end{tikzpicture}```The second figure provides the same information, yet via a networkdiagram. Again, this will not be overly meaningful until therepository contains some content.```{bash}#| label: gitDraw1#| engine: bash#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit1.png ```![](10_git_files/figure-html/drawGit1.png)## Initializing other types of repositoriesThe above demonstrated how to initialise a new local repository fromscratch. However, there are times when we instead want to:- create a git repository from an existing directory or project- collaborate with someone on an existing repository- create a remote repositoryThese situations are briefly demonstrated in the following sections.### Initializing a shared (remote) repository```{tikz}%| label: Fig1b%| engine: tikz%| echo: false%| cache: true%| dependson: common%| include: false%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\tikzstyle{refText} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}]\begin{tikzpicture}\rcommit{}{A}{white}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\master{right = 0.5cm of A}\draw[->,line width=3pt,draw=black!60] (master) -- (A);\HEAD{right = 0.5cm of master}\draw[->,line width=3pt,draw=black!60] (HEAD) -- (master);\end{tikzpicture} ``````{bash}#| label: Fig1b-conv#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Fig1b-1.pdf 10_git_files/figure-html/Fig1b-1.png ```![](10_git_files/figure-html/Fig1b-1.png)The main repository for sharing should not contain the workingdirectory as such - only the `.git` tree and the`.gitignore` file. Typically the point of a remoterepository is to act as a perminantly available repository from whichmultiple uses can exchange files. Consequently, those accessing thisrepository should only be able to interact with the .git_metadata_ - they do not directly modify any files.Since a remote repository is devode of the working files anddirectories, it is referred to as _bare_. ::: {.panel-tabset}#### TerminalTo create a _bare_ remote repository, issue the `git init --bare`_command_ after logging in to the remote location.```{bash}#| label: git-initB#| echo: true#| eval: false#| cache: false#| classes: bash#| engine: bashgit init --bare```#### RstudioUse the instructions for the `Terminal`:::### Cloning an existing repository```{tikz}%| label: Fig1c%| engine: tikz%| echo: false%| cache: true%| dependson: common%| include: false%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\tikzstyle{refText} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}]\begin{tikzpicture}\state{0,0}{color_local}{L}\node[TARGET,fill=white] at ($(L.north) +(0,0.5cm)$) {Local Repository};\commit{right = 0.5 cm of L}{A}{color_inactive}{}{}\commit{right = 1cm of A}{B}{color_commit}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- (L.east);\draw [<-,line width=3pt,draw=black!60] (A) -- (B);\master{right = 0.5cm of B}\draw[->,line width=3pt,draw=black!60] (master) -- (B);\HEAD{right = 0.5cm of master}\draw[->,line width=3pt,draw=black!60] (HEAD) -- (master);\state{$(HEAD) +(3,0)$}{color_remote}{R}\node[TARGET,fill=white] at ($(R.north) +(0,0.5cm)$) {Remote Repository};\rcommit{right = 1cm of R}{rA}{color_inactive}{}{}\commit{right = 1cm of rA}{rB}{color_commit}{}{}\draw [-,line width=3pt,draw=black!60] (rA) -- (R.east);\draw [<-,line width=3pt,draw=black!60] (rA) -- (rB);\rmaster{right = 0.5cm of rB}\draw[->,line width=3pt,draw=black!60] (rmaster) -- (rB);\rHEAD{right = 0.5cm of rmaster}\draw[->,line width=3pt,draw=black!60] (rHEAD) -- (rmaster);\draw [->,line width=3pt,draw=black!60] (rB.south west) to [out=220,in=300] (B.south east);\end{tikzpicture} ``````{bash}#| label: Fig1c-conv#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Fig1c-1.pdf 10_git_files/figure-html/Fig1c-1.png ```![](10_git_files/figure-html/Fig1c-1.png)To get your own local copy of an existing repository, issue the `gitclone <repo url>` _command_ in the root of the working directory youwish to contain the git repository. The `repo url` points to thelocation of the existing repository to be cloned. This is also a **onetime operation** and should be issued in an otherwise empty directory.The `repo url` can be located on any accessible filesytem (local orremote). The cloning process also stores a link back to the originallocation of the repository (called **origin**). This provides aconvenient way for the system to keep track of where the localrepository should exchange files.Many git repositories are hosted on sites such as github, gitlab orbitbucket. Within an online git repository, these sites provide urllinks for cloning.::: {.panel-tabset}#### Terminal```{bash}#| label: git-clone#| echo: true#| eval: false#| cache: false#| classes: bash#| engine: bashgit clone "url.git"```where `"url.git"` is the url of the hosted repository.#### Rstudio1. click on the `Project` selector in the top right of the Rstudio window (as highlighted by the red ellipse in the image below.2. select `New Project` from the dropdown menu3. select `Version Control` form the Create Project panel4. select `Git` from the Create Project from Version Control panel5. paste in the address of the repository that you want to clone, optionally a name for this repository (if you do not like the original name) and use the `Browse` button to locate a suitable position for this new directory.6. Click the `Create Project` button:::### Initializing a repository in an existing directory::: {.panel-tabset}#### TerminalThis is the same as for a new directory.```{bash}#| label: git-create1a#| echo: true#| eval: false#| cache: false#| classes: bash#| engine: bashgit init```#### Rstudio1. click on the `Project` selector in the top right of the Rstudio window (as highlighted by the red ellipse in the image below.2. select `New Project` from the dropdown menu3. select `Existing Directory` form the Create Project panel4. use the `Browse` button to locate the existing directory6. Click the `Create Project` button:::# Tracking filesThe basic workflow for tracking files is a two step process in whichone or more files are first added to the **staging area** beforethey are committed to the **local repository**. The staging areaacts as a little like a snapshot of what the repository will look likeonce the changes have been committed. The staging area also acts likea buffer between the files in the workspace (actual local copy offiles) and the local repository (committed changes).The reason that this is a two step process is that it allows the userto make edits to numerous files, yet block the commits in smallerchunks to help isolate changes in case there is a need to roll back toprevious versions.## Staging filesWhen a file is first added to the staging area, a full copy of thatfile is added to the staging area (not just the file diffs as in otherversioning systems).::: {.panel-tabset}### TerminalTo demonstrate lets create a file (a simple text file containing thestring saying 'File 1') and add it to the staging area.```{bash}#| label: git-add#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1echo'File 1'> file1```Now lets add this file to the staging area```{bash}#| label: git-add1#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git add file1```To see the status of the repository (that is, what files are beingtracked), we issue the `git status` _command_```{bash}#| label: git-status#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git status```This indicates that there is a single file (`file1`) in thestaging area### RstudioTo demonstrate lets create a file (a simple text file containing thestring saying 'File 1') and add it to the staging area.1. Click the green "New File" button followed by the "Text File" option (or click the equivalent option from the "File" menu)![](../resources/rstudio_status1.png){width=50%}2. Type `File 1` in the panel with the flashing cursor. This panel represents the contents of the yet to be named file that we are creating.![](../resources/rstudio_status2.png){width=50%}3. Click the "Save" or "Save all" buttons (or select the equivalent items from the "File" menu) and name the file "file1" Switch to the `Git` tab and you should notice a number of items (including the file we just created) in the panel. These are files that git is aware of, but not yet tracking. This panel acts as a status window. The yellow "?" symbol indicates that git considers these files "untracked"![](../resources/rstudio_status3.png){width=50%}4. To stage a file, click on the corresponding checkbox - the status symbol should change to a green "A" (for added)![](../resources/rstudio_status4.png){width=50%}:::Our simple overview schematic represents the staging of file 1.```{tikz}%| label: Fig-advanced2a%| engine: tikz%| echo: false%| cache: true%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \usetikzlibrary{arrows.meta}\tikzstyle{TARGET} = [font={\fontspec[Scale=2]{NotoSans-Regular}}] \tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}] \tikzstyle{TREE} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}] \tikzstyle{fileText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] \definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_index}{rgb}{0.78,0.86,0.27}\definecolor{color_local}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\definecolor{color_master}{rgb}{0.36,0.27,0.87}\definecolor{color_head}{HTML}{6495ED} %\definecolor{color_head}{HTML}{6495ED}% A template for making the storage symbol\newcommand{\state}[3]{\draw (#1) node [draw=none,fill=#2,shape=circle,minimum width=2cm] (#3) {\begin{tikzpicture}\node [draw=white, fill=white,shape=cylinder,shape aspect=1.3 ,shape border rotate=90,minimum height=1.6cm,minimum width=1.5cm] at (0,0) (Cylinder) {}; \draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.1cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.5cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.9cm)$) arc (-180:0:0.8cm and 0.2cm); \end{tikzpicture}};}% Define dirtree\makeatletter\newcount\dirtree@lvl\newcount\dirtree@plvl\newcount\dirtree@clvl\def\dirtree@growth{%\ifnum\tikznumberofcurrentchild=1\relax\global\advance\dirtree@plvl by 1\expandafter\xdef\csname dirtree@p@\the\dirtree@plvl\endcsname{\the\dirtree@lvl}\fi\global\advance\dirtree@lvl by 1\relax\dirtree@clvl=\dirtree@lvl\advance\dirtree@clvl by -\csname dirtree@p@\the\dirtree@plvl\endcsname\pgf@xa=0.25cm\relax\pgf@ya=-0.5cm\relax\pgf@ya=\dirtree@clvl\pgf@ya\pgftransformshift{\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%\ifnum\tikznumberofcurrentchild=\tikznumberofchildren\global\advance\dirtree@plvl by -1\fi}\tikzset{dirtree/.style={growth function=\dirtree@growth,every node/.style={anchor=north},every child node/.style={anchor=west},edge from parent path={(\tikzparentnode\tikzparentanchor) |- (\tikzchildnode\tikzchildanchor)}}}\makeatother%end of t%Define a file \newcommand{\file}[3] {\coordinate (#3) at (#1);\draw [anchor=top left](#1) rectangle ++(2,-2);\draw [fill=color_workspace]($(#1) +(2,0)$) -- ++(0,-0.5) --++(-0.5,0.5) -- cycle; \node [anchor=north west,fileText] at ($(#1) +(0.3,-0.3)$) {#2};}% end of file definition\begin{tikzpicture}\state{0,0}{color_workspace}{W}\node[TARGET,fill=white] at ($(W.north) +(0,0.5cm)$) {Workspace};\state{6,0}{color_index}{I}\node[TARGET,fill=white] at ($(I.north) +(0,0.5cm)$) {Staging Area};\state{12,0}{color_local}{L}\node[TARGET,fill=white] at ($(L.north) +(0,0.5cm)$) {Local Repository};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_index] ($(W.east) + (0.1cm,0)$) -- ($(I.west) + (-0.1cm,0)$) node[anchor=center,pos=0.5, text=black,align=center,CODE] {add};\node [anchor=north west] at ($(W) +(-1cm,-1cm)$) (W0) {\begin{tikzpicture}[dirtree,TREE]\node {} child {node {file 1}};\end{tikzpicture}};%\draw[decorate,decoration={brace,amplitude=5pt},very thick] ($(W0.north) + (-1cm,-1cm)$) -- ($(W0.south) + (-1cm,0.2cm)$);\node [anchor=north west] at ($(I) +(-1cm,-1cm)$) (W0) {\begin{tikzpicture}[dirtree,TREE,text=gray,color=gray]\node {} child {node {file 1}};\end{tikzpicture}};\end{tikzpicture} ```A schematic of the internal working of git shows in`.git/objects` a **blob** has been created. This is acompressed version of `file1`. Its filename is a 40 digit**SHA-1 checksum** has representing the contents of the`file1`. To re-iterate, the blob name is a SHA-1 hash ofthe file contents (actually, the first two digits form a folder andthe remaining 38 form the filename).We can look at the contents of this _blob_ using the `gitcat-file` _command_. This command outputs the contents of acompressed _object_ (_blob_, _tree_, _commit_)from either the objects name (or unique fraction thereof) or its_tag_ (we will discuss tags later). ```{bash}#| label: git-blob1#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git cat-file blob 50fcd```The add (staging) process also created a `index` file. This filesimply points to the _blob_ that is part of the snapshot. The gitinternals schematic illustrates the internal changes in response tostaging a file.```{cat}#| label: Fig-advanced2#| echo: true#| class: tikz #| engine.opts:#| file: "../resources/Fig-advanced2.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\usetikzlibrary{arrows.meta}\definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_tree}{HTML}{9ACD32} %\definecolor{color_tree}{rgb}{0.78,0.86,0.27}\definecolor{color_commit}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\definecolor{color_master}{rgb}{0.36,0.27,0.87}\definecolor{color_head}{HTML}{6495ED} %\definecolor{color_head}{HTML}{6495ED}\definecolor{color_index}{HTML}{E0FFFF} %\definecolor{color_index}{rgb}{1,1,1}\definecolor{color_file}{rgb}{1,1,1}\tikzstyle{TARGET} = [font={\fontspec[Scale=2]{NotoSans-Regular}}] \tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}] \tikzstyle{TREE} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}] \tikzstyle{fileText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] %Define a file \newcommand{\file}[4] {\def\corner{0.15in};\def\cornerradius{0.02in};\def\lwidth{0.02in};\def\h{0.5in};\def\w{0.85in};\def\nline{0};\def\iconmargin{0.1in};\def\topmargin{0.3in};\node at (#1) {\begin{tikzpicture}\coordinate (nw) at ($(-0.05in*1,-0.15in*1)$);\coordinate (#3) at (#1); \coordinate (ne0) at ($(nw) + (\w, 0)$);\coordinate (ne1) at ($(ne0) - (\corner, 0)$);\coordinate (ne2) at ($(ne0) - (0, \corner)$);\coordinate (se) at ($(ne0) + (0, -\h)$); \filldraw [-, line width = \lwidth, fill=#4] (nw) -- (ne1) -- (ne2)[rounded corners=\cornerradius]--(se) -- (nw|-se) -- cycle;\draw [-, line width = \lwidth] (ne1) [rounded corners=\cornerradius]-- (ne1|-ne2) -- (ne2);\node [anchor=north west,TREE] at (nw) {#2};\foreach \k in {0,...,\nline}{\draw [-, line width = \lwidth, line cap=round] ($(nw|-se) + (\iconmargin,\iconmargin) + (0,{(\k-1)/(\nline-1)*(\h - \iconmargin - \topmargin)})$)-- ++ ($(\w,0) - 2*(\iconmargin,0)$);}\end{tikzpicture}};}% end of file definition\begin{tikzpicture}\coordinate (G_ul) at (0,0);\coordinate (G_ll) at ($(G_ul) +(0,-10)$);\coordinate (G_lr) at ($(G_ll) +(4,0)$);\coordinate (G_ur) at ($(G_ul) +(4,0)$);\node[TREE,anchor=west] at ($(G_ul) +(0,-0.5)$) (git) {.git/};\file{$(git.west) +(1.5,-2)$}{HEAD}{HEAD}{color_head}\file{$(HEAD) +(0,-2)$}{index}{index}{color_index}\draw[] (G_ul) -- (G_ll) -- (G_lr) -- (G_ur) -- cycle;%refs\node[TREE,anchor=west] at ($(G_ul) +(-5,-0.5)$) (git_ref) {.git/refs/};\draw[] ($(G_ul) +(-5,0)$) -- ++(0,-10) -- ++(4.9,0) -- ++(0,10) -- cycle;%objects\node[TREE,anchor=west] at ($(G_ul) +(-17,-0.5)$) (git_object) {.git/objects/};\file{$(git_object.west) +(1.5,-2)$}{\textcolor{black}{hashblob}}{blob1}{color_workspace}\draw[] ($(G_ul) +(-17,0)$) -- ++(0,-10) -- ++(11.9,0) -- ++(0,10) -- cycle;%trees%commits%files\node[TREE,anchor=west] at ($(G_ul) +(-21,-0.5)$) (root) {/};\file{$(root.west) +(1.7,-2)$}{\textcolor{black}{file1}}{file1}{color_file}\draw [very thick] (root) -- (root |- file1) -- ++(0.2,0);\begin{pgfonlayer}{background}\draw[fill=color_workspace!20] ($(G_ul) +(-21,0)$) -- ++(0,-10) -- ++(3.9,0) -- ++(0,10) -- cycle;\end{pgfonlayer}%arrows\draw[->,very thick,dashed] ($(file1) +(1.2,0)$) -- ($(blob1) +(-1.1,0)$);\draw[->,very thick,solid] ($(index) +(-1,0)$) -- ($(blob1) +(1.2,0)$);\end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)hash = system('cd ~/tmp/Repo1; git ls-files --stage', intern=TRUE)hash = read.table(textConnection(hash))hash_blob = strtrim(hash$V2,5)system(paste0("sed -i 's/hashblob/",hash_blob,"/g' ../resources/Fig-advanced2.tikz"))system("xelatex -output-directory=../resources ../resources/Fig-advanced2.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig-advanced2.pdf ../resources/Fig-advanced2.png")```![](../resources/Fig-advanced2.png)::: {.callout-note collapse="true"}## Another visual representation of the git```{bash}#| label: gitDraw2#| engine: bash#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit2.png ```![](10_git_files/figure-html/drawGit2.png):::## Commit to local repository```{tikz}%| label: Fig2a%| engine: tikz%| echo: false%| cache: true%| dependson: common%| include: false%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\tikzstyle{refText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] \begin{tikzpicture}\commit{}{A}{color_commit}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\master{right = 0.5cm of A}\draw[->,line width=3pt,draw=black!60] (master) -- (A);\HEAD{right = 0.5cm of master}\draw[->,line width=3pt,draw=black!60] (HEAD) -- (master);\end{tikzpicture} ``````{bash}#| label: Fig2a-conv#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Fig2a-1.pdf 10_git_files/figure-html/Fig2a-1.png ```![](10_git_files/figure-html/Fig2a-1.png)::: {.panel-tabset}### TerminalTo commit a set of changes from the staging area to the localrepository, we issue the `git commit` _command_. We usually add the`-m` _switch_ to explicitly supply a message to be associated with thecommit. This message should ideally describe what the changes thecommit introduces to the repository.```{bash}#| label: git-commit#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git commit -m'Initial repo and added file1'```We now see that the status has changed. It indicates that the tree inthe workspace is in sync with the repository.```{bash}#| label: git-commit1#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git status```### RstudioTo commit a set of changes from the staging area to the localrepository:1. click on the "Commit" button to open the "Review Changes" window![](../resources/rstudio_commit1.png){width=80%} This box will list the files to be committed (in this case "file1"), the changes in this file since the previous commit (as this is the first time this file has been committed, the changes are the file contents)2. you should also provide a commit message (in the figure above, I entered "Initial commit". This message should ideally describe what the changes the commit introduces to the repository.3. click the "Commit" button and you will be presented with a popup message.![](../resources/rstudio_commit2.png) This message provides feedback to confirm that your commit was successful. 4. close the popup window and the "Review Changes" window`file1` should now have disappeared from the git status panel.:::Our simple overview schematic represents the staging of file 1.```{tikz}%| label: Fig-advanced3a%| engine: tikz%| echo: false%| cache: true%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \usetikzlibrary{arrows.meta}\tikzstyle{TARGET} = [font={\fontspec[Scale=2]{NotoSans-Regular}}] \tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}] \tikzstyle{TREE} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}] \tikzstyle{fileText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] \definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_index}{rgb}{0.78,0.86,0.27}\definecolor{color_local}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\definecolor{color_master}{rgb}{0.36,0.27,0.87}\definecolor{color_head}{HTML}{6495ED} %\definecolor{color_head}{HTML}{6495ED}% A template for making the storage symbol\newcommand{\state}[3]{\draw (#1) node [draw=none,fill=#2,shape=circle,minimum width=2cm] (#3) {\begin{tikzpicture}\node [draw=white, fill=white,shape=cylinder,shape aspect=1.3 ,shape border rotate=90,minimum height=1.6cm,minimum width=1.5cm] at (0,0) (Cylinder) {}; \draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.1cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.5cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.9cm)$) arc (-180:0:0.8cm and 0.2cm); \end{tikzpicture}};}% Define dirtree\makeatletter\newcount\dirtree@lvl\newcount\dirtree@plvl\newcount\dirtree@clvl\def\dirtree@growth{%\ifnum\tikznumberofcurrentchild=1\relax\global\advance\dirtree@plvl by 1\expandafter\xdef\csname dirtree@p@\the\dirtree@plvl\endcsname{\the\dirtree@lvl}\fi\global\advance\dirtree@lvl by 1\relax\dirtree@clvl=\dirtree@lvl\advance\dirtree@clvl by -\csname dirtree@p@\the\dirtree@plvl\endcsname\pgf@xa=0.25cm\relax\pgf@ya=-0.5cm\relax\pgf@ya=\dirtree@clvl\pgf@ya\pgftransformshift{\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%\ifnum\tikznumberofcurrentchild=\tikznumberofchildren\global\advance\dirtree@plvl by -1\fi}\tikzset{dirtree/.style={growth function=\dirtree@growth,every node/.style={anchor=north},every child node/.style={anchor=west},edge from parent path={(\tikzparentnode\tikzparentanchor) |- (\tikzchildnode\tikzchildanchor)}}}\makeatother%end of t%Define a file \newcommand{\file}[3] {\coordinate (#3) at (#1);\draw [anchor=top left](#1) rectangle ++(2,-2);\draw [fill=color_workspace]($(#1) +(2,0)$) -- ++(0,-0.5) --++(-0.5,0.5) -- cycle; \node [anchor=north west,fileText] at ($(#1) +(0.3,-0.3)$) {#2};}% end of file definition\begin{tikzpicture}\state{0,0}{color_workspace}{W}\node[TARGET,fill=white] at ($(W.north) +(0,0.5cm)$) {Workspace};\state{6,0}{color_index}{I}\node[TARGET,fill=white] at ($(I.north) +(0,0.5cm)$) {Staging Area};\state{12,0}{color_local}{L}\node[TARGET,fill=white] at ($(L.north) +(0,0.5cm)$) {Local Repository};\node [anchor=north west] at ($(W) +(-1cm,-1cm)$) (W0) {\begin{tikzpicture}[dirtree,TREE]\node {} child {node {file 1}};\end{tikzpicture}};%\draw[decorate,decoration={brace,amplitude=5pt},very thick] ($(W0.north) + (-1cm,-1cm)$) -- ($(W0.south) + (-1cm,0.2cm)$);\node [anchor=north west] at ($(W) +(-1cm,-1cm)$) (W0) {\begin{tikzpicture}[dirtree,TREE]\node {} child { node {file 1}};\end{tikzpicture}};%\draw[decorate,decoration={brace,amplitude=5pt},very thick] ($(W0.north) + (-1cm,-1cm)$) -- ($(W0.south) + (-1cm,0.2cm)$);\node [anchor=north west] at ($(I) +(-1cm,-1cm)$) (W0) {\begin{tikzpicture}[dirtree,TREE,text=gray,color=gray]\node {} child { node {file 1}};\end{tikzpicture}};\node [anchor=north west] at ($(L) +(-1cm,-1cm)$) (L0) {\begin{tikzpicture}[dirtree,TREE]\node {} child { node {file 1}};\end{tikzpicture}};\draw[decorate,decoration={brace,amplitude=5pt},very thick] ($(L0.north) + (1.3cm,-0.2cm)$) -- ($(L0.south) + (1.3cm,0.0cm)$);\node [TREE,anchor=west] at ($(L0) +(1.5,-0.1)$) {committed snapshot 1}; \end{tikzpicture}```::: {.callout-note collapse="true"}## Additional details about the commitThe following modifications have occurred (in reverse order to howthey actually occur):- The **main** _branch_ reference was created. There is currently only a single branch (more on branches later). The branch reference point to (indicates) which commit is the current commit within a branch.```{bash} #| label: git-commit1a #| echo: !expr -1 #| eval: true #| cache: false #| classes: bash #| engine: bash cd ~/tmp/Repo1 cat .git/refs/heads/main ```- A **commit** was created. This points to a **tree** (which itself points to the blob representing `file1`) as well as other important metadata (such as who made the commit and when). Since the time stamp will be unique each time a snapshot is commited, so too the name of the commit (as a SHA-1 checksum hash) will differ. **To reiterate, the names of blobs and trees are determined by contents alone, commit names are also incorporate commit timestamp and details of the committer - and are thus virtually unique**.```{r} #| label: bash1 #| echo: false #| eval: true #| cache: false hash = scan('~/tmp/Repo1/.git/refs/heads/main', what='character') hash_commit = strtrim(hash, width=5) knit_engines$set(bash1=function(options) { engine=options$engine options$code = gsub('#',hash_commit,options$code) code = paste("bash -c",shQuote(paste(options$code, collapse="\n"))) code = paste(options$engine_opts,code) out = system(code,intern=TRUE) engine_output(options,options$code,out) }) ``````{r, replace=TRUE} #| label: git-commit1b #| echo: !expr -1 #| eval: false #| cache: false #| classes: bash #| engine: bash1 cd ~/tmp/Repo1 git cat-file commit # ```- A **tree** object was created. This represents the directory tree of the snapshot (commit) and thus points to the **blob**s.```{r, replace=TRUE} #| label: git-commit1c #| echo: !expr -1 #| eval: true #| cache: false #| classes: bash #| engine: bash1 cd ~/tmp/Repo1 git ls-tree # ``` Or most commonly (if interested in the latest commit):```{r, replace=FALSE} #| label: git-commit1d #| echo: !expr -1 #| eval: true #| cache: false #| classes: bash #| engine: bash1 cd ~/tmp/Repo1 git ls-tree HEAD ```::: The schematic now looks like```{cat}#| label: Fig-advanced3#| echo: true#| class: tikz #| engine.opts:#| file: "../resources/Fig-advanced3.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\usetikzlibrary{arrows.meta}\definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_tree}{HTML}{9ACD32} %\definecolor{color_tree}{rgb}{0.78,0.86,0.27}\definecolor{color_commit}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\definecolor{color_master}{rgb}{0.36,0.27,0.87}\definecolor{color_head}{HTML}{6495ED} %\definecolor{color_head}{HTML}{6495ED}\definecolor{color_index}{HTML}{E0FFFF} %\definecolor{color_index}{rgb}{1,1,1}\definecolor{color_file}{rgb}{1,1,1}\tikzstyle{TARGET} = [font={\fontspec[Scale=2]{NotoSans-Regular}}] \tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}] \tikzstyle{TREE} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}] \tikzstyle{fileText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] %Define a file \newcommand{\file}[4] {\def\corner{0.15in};\def\cornerradius{0.02in};\def\lwidth{0.02in};\def\h{0.5in};\def\w{0.85in};\def\nline{0};\def\iconmargin{0.1in};\def\topmargin{0.3in};\node at (#1) {\begin{tikzpicture}\coordinate (nw) at ($(-0.05in*1,-0.15in*1)$);\coordinate (#3) at (#1); \coordinate (ne0) at ($(nw) + (\w, 0)$);\coordinate (ne1) at ($(ne0) - (\corner, 0)$);\coordinate (ne2) at ($(ne0) - (0, \corner)$);\coordinate (se) at ($(ne0) + (0, -\h)$); \filldraw [-, line width = \lwidth, fill=#4] (nw) -- (ne1) -- (ne2)[rounded corners=\cornerradius]--(se) -- (nw|-se) -- cycle;\draw [-, line width = \lwidth] (ne1) [rounded corners=\cornerradius]-- (ne1|-ne2) -- (ne2);\node [anchor=north west,TREE] at (nw) {#2};\foreach \k in {0,...,\nline}{\draw [-, line width = \lwidth, line cap=round] ($(nw|-se) + (\iconmargin,\iconmargin) + (0,{(\k-1)/(\nline-1)*(\h - \iconmargin - \topmargin)})$)-- ++ ($(\w,0) - 2*(\iconmargin,0)$);}\end{tikzpicture}};}% end of file definition\begin{tikzpicture}\coordinate (G_ul) at (0,0);\coordinate (G_ll) at ($(G_ul) +(0,-10)$);\coordinate (G_lr) at ($(G_ll) +(4,0)$);\coordinate (G_ur) at ($(G_ul) +(4,0)$);\node[TREE,anchor=west] at ($(G_ul) +(0,-0.5)$) (git) {.git/};\file{$(git.west) +(1.5,-2)$}{HEAD}{HEAD}{color_head}\file{$(HEAD) +(0,-2)$}{index}{index}{color_index}\draw[] (G_ul) -- (G_ll) -- (G_lr) -- (G_ur) -- cycle;%refs\node[TREE,anchor=west] at ($(G_ul) +(-5,-0.5)$) (git_ref) {.git/refs/};\file{$(git_ref.west) +(1.5,-2)$}{\textcolor{white}{master}}{master}{color_master}\draw[] ($(G_ul) +(-5,0)$) -- ++(0,-10) -- ++(4.9,0) -- ++(0,10) -- cycle;%objects\node[TREE,anchor=west] at ($(G_ul) +(-17,-0.5)$) (git_object) {.git/objects/};\file{$(git_object.west) +(1.5,-2)$}{\textcolor{black}{hashblob}}{blob1}{color_workspace}\draw[] ($(G_ul) +(-17,0)$) -- ++(0,-10) -- ++(11.9,0) -- ++(0,10) -- cycle;%trees\file{$(blob1.west) +(5,0)$}{\textcolor{black}{hashtree}}{tree1}{color_tree}%commits\file{$(tree1.west) +(3,0)$}{\textcolor{black}{hashcommit}}{commit1}{color_commit}%files\node[TREE,anchor=west] at ($(G_ul) +(-21,-0.5)$) (root) {/};\file{$(root.west) +(1.7,-2)$}{\textcolor{black}{file1}}{file1}{color_file}\draw [very thick] (root) -- (root |- file1) -- ++(0.2,0);\begin{pgfonlayer}{background}\draw[fill=color_workspace!20] ($(G_ul) +(-21,0)$) -- ++(0,-10) -- ++(3.9,0) -- ++(0,10) -- cycle;\end{pgfonlayer}%arrows\draw[->,very thick] ($(HEAD) +(-1,0)$) -- ($(master) +(1.2,0)$);\draw[->,very thick] ($(master) +(-1,0)$) -- ($(commit1) +(1.2,0)$);\draw[->,very thick] ($(commit1) +(-1,0)$) -- ($(tree1) +(1.2,0)$);\draw[->,very thick] ($(tree1) +(-1,0)$) -- ($(blob1) +(1.2,0)$);\draw[->,very thick,solid] ($(index) +(-1,0)$) -- ($(blob1) +(1.2,0)$);\end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)#commithash = scan('~/tmp/Repo1/.git/refs/heads/main', what='character')hash_commit = strtrim(hash, width=5)#treehash = system(paste0('cd ~/tmp/Repo1; git cat-file -p ',hash_commit), intern=TRUE)hash = read.table(textConnection(hash[1]))hash_tree = strtrim(hash$V2,5)#blobhash = system('cd ~/tmp/Repo1; git ls-files --stage', intern=TRUE)hash = read.table(textConnection(hash))hash_blob = strtrim(hash$V2,5)system(paste0("sed -i 's/hashblob/",hash_blob,"/g' ../resources/Fig-advanced3.tikz"))system(paste0("sed -i 's/hashtree/",hash_tree,"/g' ../resources/Fig-advanced3.tikz"))system(paste0("sed -i 's/hashcommit/",hash_commit,"/g' ../resources/Fig-advanced3.tikz"))system("xelatex -output-directory=../resources ../resources/Fig-advanced3.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig-advanced3.pdf ../resources/Fig-advanced3.png")```![](../resources/Fig-advanced3.png)::: {.callout-note collapse="true"}## Another visual representation of the git```{bash}#| label: gitDraw3#| engine: bash#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit3.png ```![](10_git_files/figure-html/drawGit3.png):::::: {.callout-note collapse="true"}## More information about committing changes to the repositoryCommitting staged changes creates an object under the `.git` tree.```{r, replace=TRUE}#| label: git-add3a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -a --charset unicode``````{r, replace=TRUE}#| label: git-add3b#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git cat-file -p HEAD``````{r, replace=TRUE}#| label: git-add3c#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git cat-file -p HEAD^{tree}``````{r, replace=TRUE}#| label: git-add3d#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --oneline```:::## More changes```{tikz}%| label: Fig2b%| engine: tikz%| echo: false%| cache: true%| dependson: common%| include: false%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\tikzstyle{refText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] \begin{tikzpicture}\commit{}{A}{color_inactive}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\commit{right = 1cm of A}{B}{color_commit}{}{}\draw [-,line width=3pt,draw=black!60] (B) -- (A);\master{right = 0.5cm of B}\draw[->,line width=3pt,draw=black!60] (master) -- (B);\HEAD{right = 0.5cm of master}\draw[->,line width=3pt,draw=black!60] (HEAD) -- (master);\end{tikzpicture} ``````{bash}#| label: Fig2b-conv#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Fig2b-1.pdf 10_git_files/figure-html/Fig2b-1.png ```![](10_git_files/figure-html/Fig2b-1.png)Whenever a file is added or modified, if the changes are to betracked, the file needs to be added to the staging area. Letsdemonstrate by modifying `file1` and adding an additionalfile (`file2`), this time to a subdirectory (`dir1`).::: {.panel-tabset}### Terminal```{bash}#| label: git-add4#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1echo'---------------'>> file1mkdir dir1echo'* Notes'> dir1/file2git add file1 dir1/file2```Now if we re-examine the status:```{bash}#| label: git-add5#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git status```### Rstudio1. modify `file1` by adding a number of hyphens under the `File 1` like in the figure below![](../resources/rstudio_commit3.png)2. save the file. As you do so, you should notice that the file reappears in the status panel (this time with a blue "M" to signify that the file has been modified)3. to create the subdirectory, click on the "Add a new folder" icon and then enter a name for the subdirectory in the popup box (as per figure below)![](../resources/rstudio_commit4.png)4. navigate to this new directory (`dir1`)5. click the "Create a new blank file in current directory" button and select "Text file"6. enter a new filename (`file2`) into the popup box7. enter some text into this file (like in the figure below)![](../resources/rstudio_commit5.png)8. save the file and notice that the `dir1` directory is now also in the git status panel (yet its status is "untracked")9. stage both `file1` and `dir1` (click on the corresponding checkboxes)![](../resources/rstudio_commit6.png):::And now our schematic looks like:```{cat}#| label: Fig-advanced4#| echo: true#| class: tikz #| engine.opts:#| file: "../resources/Fig-advanced4.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\usetikzlibrary{arrows.meta}\definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_tree}{HTML}{9ACD32} %\definecolor{color_tree}{rgb}{0.78,0.86,0.27}\definecolor{color_commit}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\definecolor{color_master}{rgb}{0.36,0.27,0.87}\definecolor{color_head}{HTML}{6495ED} %\definecolor{color_head}{HTML}{6495ED}\definecolor{color_index}{HTML}{E0FFFF} %\definecolor{color_index}{rgb}{1,1,1}\definecolor{color_file}{rgb}{1,1,1}\tikzstyle{TARGET} = [font={\fontspec[Scale=2]{NotoSans-Regular}}] \tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}] \tikzstyle{TREE} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}] \tikzstyle{fileText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] %Define a file \newcommand{\file}[4] {\def\corner{0.15in};\def\cornerradius{0.02in};\def\lwidth{0.02in};\def\h{0.5in};\def\w{0.85in};\def\nline{0};\def\iconmargin{0.1in};\def\topmargin{0.3in};\node at (#1) {\begin{tikzpicture}\coordinate (nw) at ($(-0.05in*1,-0.15in*1)$);\coordinate (#3) at (#1); \coordinate (ne0) at ($(nw) + (\w, 0)$);\coordinate (ne1) at ($(ne0) - (\corner, 0)$);\coordinate (ne2) at ($(ne0) - (0, \corner)$);\coordinate (se) at ($(ne0) + (0, -\h)$); \filldraw [-, line width = \lwidth, fill=#4] (nw) -- (ne1) -- (ne2)[rounded corners=\cornerradius]--(se) -- (nw|-se) -- cycle;\draw [-, line width = \lwidth] (ne1) [rounded corners=\cornerradius]-- (ne1|-ne2) -- (ne2);\node [anchor=north west,TREE] at (nw) {#2};\foreach \k in {0,...,\nline}{\draw [-, line width = \lwidth, line cap=round] ($(nw|-se) + (\iconmargin,\iconmargin) + (0,{(\k-1)/(\nline-1)*(\h - \iconmargin - \topmargin)})$)-- ++ ($(\w,0) - 2*(\iconmargin,0)$);}\end{tikzpicture}};}% end of file definition\begin{tikzpicture}\coordinate (G_ul) at (0,0);\coordinate (G_ll) at ($(G_ul) +(0,-10)$);\coordinate (G_lr) at ($(G_ll) +(4,0)$);\coordinate (G_ur) at ($(G_ul) +(4,0)$);\node[TREE,anchor=west] at ($(G_ul) +(0,-0.5)$) (git) {.git/};\file{$(git.west) +(1.5,-2)$}{HEAD}{HEAD}{color_head}\file{$(HEAD) +(0,-2)$}{index}{index}{color_index}\draw[] (G_ul) -- (G_ll) -- (G_lr) -- (G_ur) -- cycle;%refs\node[TREE,anchor=west] at ($(G_ul) +(-5,-0.5)$) (git_ref) {.git/refs/};\file{$(git_ref.west) +(1.5,-2)$}{\textcolor{white}{master}}{master}{color_master}\draw[] ($(G_ul) +(-5,0)$) -- ++(0,-10) -- ++(4.9,0) -- ++(0,10) -- cycle;%files\node[TREE,anchor=west] at ($(G_ul) +(-21,-0.5)$) (root) {/};\file{$(root.west) +(1.7,-2)$}{\textcolor{black}{file1}}{file1}{color_file}%\file{$(root.west) +(1.7,-3.5)$}{\textcolor{black}{file2}}{file2}{color_file}\node[TREE,anchor=west] at ($(root |- file1) +(0.4,-2.5)$) (dir1) {dir1/};\draw [very thick] (root) -- (root |- file1) -- ++(0.2,0);%\draw [very thick] (root) -- (root |- file2) -- ++(0.2,0);\draw [very thick] (root) -- (root |- dir1) -- ++(0.2,0);\file{$(dir1.west) +(1.7,-1.0)$}{\textcolor{black}{file2}}{file2}{color_file}\draw [very thick] ($(dir1.west) +(0.2,-0.2)$) -- ($(dir1.west |- file2) +(0.2,0)$) -- ++(0.2,0);\begin{pgfonlayer}{background}\draw[fill=color_workspace!20] ($(G_ul) +(-21,0)$) -- ++(0,-10) -- ++(3.9,0) -- ++(0,10) -- cycle;\end{pgfonlayer}%objects\node[TREE,anchor=west] at ($(G_ul) +(-17,-0.5)$) (git_object) {.git/objects/};\file{$(git_object.west) +(1.5,-2)$}{\textcolor{black}{hashblob1}}{blob1}{color_workspace}\file{$(git_object.west) +(1.5,-3.5)$}{\textcolor{black}{hashblob3}}{blob2}{color_workspace}\file{$(git_object.west |- file2.north) +(1.5,-0)$}{\textcolor{black}{hashblob2}}{blob3}{color_workspace}\draw[] ($(G_ul) +(-17,0)$) -- ++(0,-10) -- ++(11.9,0) -- ++(0,10) -- cycle;%trees\file{$(blob1.west) +(5,0)$}{\textcolor{black}{hashtree}}{tree1}{color_tree}%commits\file{$(tree1.west) +(3,0)$}{\textcolor{black}{hashcommit}}{commit1}{color_commit}%arrows\draw[->,very thick] ($(HEAD) +(-1,0)$) -- ($(master) +(1.2,0)$);\draw[->,very thick] ($(master) +(-1,0)$) -- ($(commit1) +(1.2,0)$);\draw[->,very thick] ($(commit1) +(-1,0)$) -- ($(tree1) +(1.2,0)$);\draw[->,very thick] ($(tree1) +(-1,0)$) -- ($(blob1) +(1.2,0)$);\draw[<-,very thick,dashed] ($(blob2) +(-1.2,0)$) -- ($(file1) +(1.2,0)$);\draw[<-,very thick,dashed] ($(blob3) +(-1.2,0)$) -- ($(file2) +(1.2,0)$);\draw[->,very thick] ($(index) +(-1,0)$) -- ($(blob3) +(1.2,0)$);\draw[->,very thick] ($(index) +(-1,0)$) -- ($(blob2) +(1.2,0)$);\end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)#commit hash = scan('~/tmp/Repo1/.git/refs/heads/main', what='character')hash_commits = strtrim(hash, width = 5)#treehash = system(paste0('cd ~/tmp/Repo1; git cat-file -p ',hash_commits), intern=TRUE)hash = read.table(textConnection(hash[1]))hash_tree = strtrim(hash$V2, 5)#blobhash = system('cd ~/tmp/Repo1; git ls-files --stage', intern=TRUE)hash = read.table(textConnection(hash))hash_blob = c(hash_blob,strtrim(hash$V2,5))#hash_blob = strtrim(hash$V2,5)system(paste0("sed -i 's/hashblob1/", hash_blob[1], "/g' ../resources/Fig-advanced4.tikz"))system(paste0("sed -i 's/hashblob2/", hash_blob[2], "/g' ../resources/Fig-advanced4.tikz"))system(paste0("sed -i 's/hashblob3/", hash_blob[3], "/g' ../resources/Fig-advanced4.tikz"))system(paste0("sed -i 's/hashtree/",hash_tree,"/g' ../resources/Fig-advanced4.tikz"))system(paste0("sed -i 's/hashcommit/",hash_commits,"/g' ../resources/Fig-advanced4.tikz"))system("xelatex -output-directory=../resources ../resources/Fig-advanced4.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig-advanced4.pdf ../resources/Fig-advanced4.png")```![](../resources/Fig-advanced4.png)::: {.callout-note collapse="true"}## Another visual representation of the git```{bash}#| label: gitDraw4#| engine: bash#| cache: false#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit4.png ```![](10_git_files/figure-html/drawGit4.png):::::: {.callout-note collapse="true"}## More information about staging changes to the repositorySo when **staging**, the following has been performed:- the `index` file has been updated```{bash} #| label: git-modify1 #| echo: !expr -1 #| eval: true #| cache: false #| classes: bash #| engine: bash cd ~/tmp/Repo1 git ls-files --stage ```- two new _blobs_ have been generated. One representing the modified`file1` and the other representing the newly created `file2` in the`dir1` folder. The _blob_ that represented the original `file1` contents is still present and indeed is still the one currently committed. _Blobs_ are not erased or modified.:::Now we will commit this snapshot.::: {.panel-tabset}### Terminal```{bash}#| label: git-commit5#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git commit -m'Modified file1 and added file2 (in dir1)'```### Rstudio1. click the "Commit" button2. you might like to explore the changes associated with each file3. enter a commit message (as in the figure below)![](../resources/rstudio_commit7.png)4. click the "Commit" button5. after checking that the "Git Commit" popup does not contain any errors, close the popup6. to explore the repository history, click towards the "History" button on the top left corner of the "Review Changes" window![](../resources/rstudio_commit8.png) This provides a graphical list of commits (in reverse chronological order)7. once you have finished exploring the history, you can close the "Review Changes" window:::::: {.callout-note collapse="true"}## More information about changes to the repositoryThe following modifications occur:- the _master_ branch now points to the new _commit_.```{bash} #| label: git-commit5a #| echo: !expr -1 #| eval: true #| cache: false #| classes: bash #| engine: bash cd ~/tmp/Repo1 cat .git/refs/heads/main ``````{bash} #| label: git-commit5b #| echo: !expr -1 #| eval: true #| cache: false #| classes: bash #| engine: bash cd ~/tmp/Repo1 git reflog ```- a new _commit_ was created. This points to a new root _tree_ object and also points to the previous _commit_ (its _parent_).```{r} #| label: bash2 #| echo: false #| eval: true #| cache: false hash = scan('~/tmp/Repo1/.git/refs/heads/main', what='character') hash_commit = strtrim(hash, width=5) knit_engines$set(bash1=function(options) { engine=options$engine options$code = gsub('#',hash_commit,options$code) code = paste("bash -c",shQuote(paste(options$code, collapse="\n"))) code = paste(options$engine_opts,code) out = system(code,intern=TRUE) engine_output(options,options$code,out) }) ``````{r, replace=TRUE} #| label: git-commit2b #| echo: !expr -1 #| eval: false #| cache: false #| classes: bash #| engine: bash1 cd ~/tmp/Repo1 git cat-file commit # ```- new root _tree_ was created. This points to a _blob_ representing the modified `file1` as well as a newly created sub-directory _tree_ representing the `dir1` folder.```{r, replace=TRUE} #| label: git-commit2c #| echo: !expr -1 #| eval: false #| cache: false #| classes: bash #| engine: bash1 cd ~/tmp/Repo1 git ls-tree 2b61e ``````{r, replace=TRUE} #| label: git-commit2d #| echo: !expr -1 #| eval: false #| cache: false #| classes: bash #| engine: bash1 cd ~/tmp/Repo1 git cat-file -p HEAD^{tree} ```- a new sub-directory root _tree_ was created. This points to a _blob_ representing the modified `file1` as well as a newly created subtree _tree_ representing the`file2` file within the `dir1` folder.```{r, replace=TRUE} #| label: git-commit2e #| echo: !expr -1 #| eval: false #| cache: false #| classes: bash #| engine: bash1 cd ~/tmp/Repo1 git ls-tree # ``` OR,```{r, replace=TRUE} #| label: git-commit2f #| echo: !expr -1 #| eval: false #| cache: false #| classes: bash #| engine: bash1 cd ~/tmp/Repo1 git ls-tree HEAD ```:::```{cat}#| label: Fig-advanced5#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig-advanced5.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\usetikzlibrary{arrows.meta}\definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_tree}{HTML}{9ACD32} %\definecolor{color_tree}{rgb}{0.78,0.86,0.27}\definecolor{color_commit}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\definecolor{color_master}{rgb}{0.36,0.27,0.87}\definecolor{color_head}{HTML}{6495ED} %\definecolor{color_head}{HTML}{6495ED}\definecolor{color_index}{HTML}{E0FFFF} %\definecolor{color_index}{rgb}{1,1,1}\definecolor{color_file}{rgb}{1,1,1}\tikzstyle{TARGET} = [font={\fontspec[Scale=2]{NotoSans-Regular}}] \tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}] \tikzstyle{TREE} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}] \tikzstyle{fileText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] %Define a file \newcommand{\file}[4] {\def\corner{0.15in};\def\cornerradius{0.02in};\def\lwidth{0.02in};\def\h{0.5in};\def\w{0.85in};\def\nline{0};\def\iconmargin{0.1in};\def\topmargin{0.3in};\node at (#1) {\begin{tikzpicture}\coordinate (nw) at ($(-0.05in*1,-0.15in*1)$);\coordinate (#3) at (#1); \coordinate (ne0) at ($(nw) + (\w, 0)$);\coordinate (ne1) at ($(ne0) - (\corner, 0)$);\coordinate (ne2) at ($(ne0) - (0, \corner)$);\coordinate (se) at ($(ne0) + (0, -\h)$); \filldraw [-, line width = \lwidth, fill=#4] (nw) -- (ne1) -- (ne2)[rounded corners=\cornerradius]--(se) -- (nw|-se) -- cycle;\draw [-, line width = \lwidth] (ne1) [rounded corners=\cornerradius]-- (ne1|-ne2) -- (ne2);\node [anchor=north west,TREE] at (nw) {#2};\foreach \k in {0,...,\nline}{\draw [-, line width = \lwidth, line cap=round] ($(nw|-se) + (\iconmargin,\iconmargin) + (0,{(\k-1)/(\nline-1)*(\h - \iconmargin - \topmargin)})$)-- ++ ($(\w,0) - 2*(\iconmargin,0)$);}\end{tikzpicture}};}% end of file definition\begin{tikzpicture}\coordinate (G_ul) at (0,0);\coordinate (G_ll) at ($(G_ul) +(0,-10)$);\coordinate (G_lr) at ($(G_ll) +(4,0)$);\coordinate (G_ur) at ($(G_ul) +(4,0)$);\node[TREE,anchor=west] at ($(G_ul) +(0,-0.5)$) (git) {.git/};\file{$(git.west) +(1.5,-2)$}{HEAD}{HEAD}{color_head}\file{$(HEAD) +(0,-2)$}{index}{index}{color_index}\draw[] (G_ul) -- (G_ll) -- (G_lr) -- (G_ur) -- cycle;%refs\node[TREE,anchor=west] at ($(G_ul) +(-5,-0.5)$) (git_ref) {.git/refs/};\file{$(git_ref.west) +(1.5,-2)$}{\textcolor{white}{master}}{master}{color_master}\draw[] ($(G_ul) +(-5,0)$) -- ++(0,-10) -- ++(4.9,0) -- ++(0,10) -- cycle;%files\node[TREE,anchor=west] at ($(G_ul) +(-21,-0.5)$) (root) {/};\file{$(root.west) +(1.7,-2)$}{\textcolor{black}{file1}}{file1}{color_file}\node[TREE,anchor=west] at ($(root |- file1) +(0.4,-2.5)$) (dir1) {dir1/};\draw [very thick] (root) -- (root |- file1) -- ++(0.2,0);\draw [very thick] (root) -- (root |- dir1) -- ++(0.2,0);\file{$(dir1.west) +(1.7,-1.0)$}{\textcolor{black}{file2}}{file2}{color_file}\draw [very thick] ($(dir1.west) +(0.2,-0.2)$) -- ($(dir1.west |- file2) +(0.2,0)$) -- ++(0.2,0);\begin{pgfonlayer}{background}\draw[fill=color_workspace!20] ($(G_ul) +(-21,0)$) -- ++(0,-10) -- ++(3.9,0) -- ++(0,10) -- cycle;\end{pgfonlayer}%objects\node[TREE,anchor=west] at ($(G_ul) +(-17,-0.5)$) (git_object) {.git/objects/};\file{$(git_object.west) +(1.5,-2)$}{\textcolor{black}{hashblob1}}{blob1}{color_workspace}\file{$(git_object.west) +(1.5,-3.5)$}{\textcolor{black}{hashblob3}}{blob2}{color_workspace}\file{$(git_object.west |- file2.north) +(1.5,-0)$}{\textcolor{black}{hashblob2}}{blob3}{color_workspace}\draw[] ($(G_ul) +(-17,0)$) -- ++(0,-10) -- ++(11.9,0) -- ++(0,10) -- cycle;%trees\file{$(blob1.west) +(5,0)$}{\textcolor{black}{hashtree1}}{tree1}{color_tree}\file{$($(blob3.west) !0.5! (blob3.west)$) +(3,0)$}{\textcolor{black}{hashtree3}}{tree2}{color_tree}\file{$($(blob2.west) !0.5! (blob3.west)$) +(5.75,0)$}{\textcolor{black}{hashtree2}}{tree3}{color_tree}%commits\file{$(tree1.west) +(3,0)$}{\textcolor{black}{hashcommits1}}{commit1}{color_commit}\file{$(tree3.west) +(2.75,0)$}{\textcolor{black}{hashcommits2}}{commit2}{color_commit}%arrows\draw[->,very thick] ($(HEAD) +(-1,0)$) -- ($(master) +(1.2,0)$);\draw[->,very thick] ($(master) +(-1,0)$) -- ($(commit2) +(1.2,0)$);\draw[->,very thick] ($(commit1) +(-1,0)$) -- ($(tree1) +(1.2,0)$);\draw[->,very thick] ($(tree1) +(-1,0)$) -- ($(blob1) +(1.2,0)$);\draw[->,very thick] ($(commit2) +(-1,0)$) -- ($(tree3) +(1.2,0)$);\draw[->,very thick] ($(tree3) +(-1,0)$) -- ($(tree2) +(1.2,0)$);\draw[->,very thick] ($(tree3) +(-1,0)$) -- ($(blob2) +(1.2,0)$);\draw[->,very thick] ($(tree2) +(-1,0)$) -- ($(blob3) +(1.2,0)$);\draw[->,very thick] ($(commit2) +(0,0.6)$) -- ($(commit1) +(0,-0.7)$);\draw[->,very thick] ($(index) +(-1,0)$) -- ($(blob3) +(1.2,0)$);\draw[->,very thick] ($(index) +(-1,0)$) -- ($(blob2) +(1.2,0)$);\end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)# commithash = scan("~/tmp/Repo1/.git/refs/heads/main", what = "character")hash_commits = c(hash_commits, strtrim(hash, width=5))# treehash = system(paste0("cd ~/tmp/Repo1; git cat-file -p ", hash_commits[2]), intern = TRUE)hash = read.table(textConnection(hash[1]))hash_tree = c(hash_tree, strtrim(hash$V2, 5))hash=system(paste0('cd ~/tmp/Repo1; git cat-file -p HEAD^{tree}'), intern=TRUE)hash = read.table(textConnection(hash))hash_tree = c(hash_tree, strtrim(hash$V3[hash$V2 == "tree"], 5))#blobhash = system('cd ~/tmp/Repo1; git ls-files --stage', intern=TRUE)hash = read.table(textConnection(hash))hash_blob = unique(c(hash_blob,strtrim(hash$V2,5)))system(paste0("sed -i 's/hashblob1/", hash_blob[1], "/g' ../resources/Fig-advanced5.tikz"))system(paste0("sed -i 's/hashblob2/", hash_blob[2], "/g' ../resources/Fig-advanced5.tikz"))system(paste0("sed -i 's/hashblob3/", hash_blob[3], "/g' ../resources/Fig-advanced5.tikz"))system(paste0("sed -i 's/hashtree1/", hash_tree[1], "/g' ../resources/Fig-advanced5.tikz"))system(paste0("sed -i 's/hashtree2/", hash_tree[2], "/g' ../resources/Fig-advanced5.tikz"))system(paste0("sed -i 's/hashtree3/", hash_tree[3], "/g' ../resources/Fig-advanced5.tikz"))system(paste0("sed -i 's/hashcommits1/", hash_commits[1], "/g' ../resources/Fig-advanced5.tikz"))system(paste0("sed -i 's/hashcommits2/", hash_commits[2], "/g' ../resources/Fig-advanced5.tikz"))system("xelatex -output-directory=../resources ../resources/Fig-advanced5.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig-advanced5.pdf ../resources/Fig-advanced5.png")```![](../resources/Fig-advanced5.png)::: {.callout-note collapse="true"}## Another visual representation of the git```{bash}#| label: gitDraw5#| engine: bash#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit5.png ```![](10_git_files/figure-html/drawGit5.png){width=100%}:::::: {.callout-note collapse="true"}## More information about committing changes to the repositoryCommitting staged changes creates an object under the `.git` tree.```{r, replace=TRUE}#| label: git-add5a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -a --charset unicode``````{r, replace=TRUE}#| label: git-add5b#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git cat-file -p HEAD``````{r, replace=TRUE}#| label: git-add5c#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git cat-file -p HEAD^{tree}``````{r, replace=TRUE}#| label: git-add5d#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --oneline```:::Now you might be wondering... What if I have modified many files and Iwant to stage them all. Do I really have to add each fileindividually? Is there not some way to add multiple files at a time?The answer of course is yes. To stage all files (including those insubdirectories) we issue the `git add .` _command_ (notice the dot).```{bash}#| label: git-add6#| echo: !expr -1#| eval: false#| cache: false#| classes: bashcd ~/tmp/Repo1git add .```## `.gitignore`Whilst it is convenient to not have to list every file that you wantto be staged (added), what about files that we don't want to getstaged and committed. It is also possible to define a file (called`.gitignore`) that is a list of files (or file patterns) that are tobe excluded when we request all files be added. This functionality isprovided via the `.gitignore` _file_ that must be in the root of therepository working directory.For example, we may have temporary files or automatic backup files orfiles generated as intermediates in a compile process etc that getgenerated. These files are commonly generated in the process ofworking with files in a project, yet we do not necessarily wish forthem to be tracked. Often these files have very predictable filenamepattern (such as ending with a # or ~ symbol or having a specific fileextension such as .aux. As an example, when working with a project in Rstudio, files (such as`.Rhistory`) and directories (such as `.Rproj.user`) are automaticallyadded to the file system and thus appear as untracked files in gitstatus.Hence, we can create a`.gitignore` to exclude these files/directories.Indeed, if you are using Rstudio, you might have noticed that a`.gitignore` file was automatically created when you created theproject.Lets start by modifying the `file2` and creating a new file`f.tmp` (that we want to ignore).::: {.panel-tabset}### Terminal```{bash}#| label: git-add7#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1echo'---'>> dir1/file2echo'temp'> dir1/f.tmp```### Rstudio1. navigate to the `dir1` directory and open `file2` for editing (or just make sure you are on the `file2` tab.2. edit the file such that it just contains three hyphens (`---`) before saving the file3. in the same `dir1` directory add another new text file (`f.tmp`) and edit this file to contain the word `temp` (then save the file)The Git status panel should display both of these as untracked files.![](../resources/rstudio_gitignore1.png):::To ignore the `f.tmp` file, we could either explicitly addthis file as a row in a `.gitignore` file, or else we couldsupply a wildcard version that will ignore all files ending in`.tmp`.::: {.panel-tabset}### Terminal```{bash}#| label: git-ignore1#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1echo'*.tmp'> .gitignorecat .gitignore```### Rstudio1. navigate back to the root of the project2. click on the `gitignore` file to open it up for editing3. navigate to the end of this file and add a newline containing the text `*.tmp`![](../resources/rstudio_gitignore2.png)You will notice that this `.gitignore` file already had items in itbefore you started editing it. These were added by Rstudio when youfirst created the new project.The first item is `.Rproj.user` and its presence in this file is whyit does not appear in the git status panel.Once we save the `.gitignore` file, notice how the `f.tmp` file issimilarly removed from the git status panel - since via `.gitignore`we have indicated that we want to ignore this file (not track it aspart of our version control system).:::::: {.callout-note collapse="true"}## More information about exclusions (`.gitignore`)| Entry | Meaning ||----------------|------------------------------------------------------------------------------------------------------------------------|| `file1` | DO NOT stage (add) file1 || `*.tmp` | DO NOT stage (add) any file ending in .tmp || `/dir1/*` | DO NOT stage (add) the folder called dir1 (or any of its contents) unless this is specifically negated (see next line) || `!/dir1/file2` | DO stage (add) the file called file2 in the dir1 folder |: {.primary .bordered .sm .paramsTable}:::Now when we go to add all files to the staging area, those that fallunder the exclude rules will be _ignored_::: {.panel-tabset}### Terminal```{bash}#| label: gitgitignore2#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1git add .``````{bash}#| label: gitgitignore3#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1git status```You will notice that `.gitignore` was added as a _new file_ and`dir1/file2` was marked as _modified_ yet `dir1/f.tmp` was totallyignored.### RstudioYou will notice that `.gitignore` was added as a _new file_ and`dir1/file2` was marked as _modified_ yet `dir1/f.tmp` was totallyignored.1. check the boxes next to each of the files listed in the status panel![](../resources/rstudio_gitignore3.png):::Lets now commit these changes.::: {.panel-tabset}### Terminal```{bash}#| label: gitgitignore4#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1git commit -m'Modified file2, added .gitignore'``````{bash}#| label: gitgitignore5#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1git status```### Rstudio1. click on the "Commit" button2. add a commit message (such as `Modified file2, added .gitignore`)3. click the "Commit" button4. close the popup5. close the "Review Changes" window:::For those still interested in the schematic...```{cat}#| label: Fig-advanced6#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig-advanced6.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\usetikzlibrary{arrows.meta}\definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_tree}{HTML}{9ACD32} %\definecolor{color_tree}{rgb}{0.78,0.86,0.27}\definecolor{color_commit}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\definecolor{color_master}{rgb}{0.36,0.27,0.87}\definecolor{color_head}{HTML}{6495ED} %\definecolor{color_head}{HTML}{6495ED}\definecolor{color_index}{HTML}{E0FFFF} %\definecolor{color_index}{rgb}{1,1,1}\definecolor{color_file}{rgb}{1,1,1}\tikzstyle{TARGET} = [font={\fontspec[Scale=2]{NotoSans-Regular}}] \tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}] \tikzstyle{TREE} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}] \tikzstyle{fileText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] %Define a file \newcommand{\file}[4] {\def\corner{0.15in};\def\cornerradius{0.02in};\def\lwidth{0.02in};\def\h{0.5in};\def\w{0.85in};\def\nline{0};\def\iconmargin{0.1in};\def\topmargin{0.3in};\node at (#1) {\begin{tikzpicture}\coordinate (nw) at ($(-0.05in*1,-0.15in*1)$);\coordinate (#3) at (#1); \coordinate (ne0) at ($(nw) + (\w, 0)$);\coordinate (ne1) at ($(ne0) - (\corner, 0)$);\coordinate (ne2) at ($(ne0) - (0, \corner)$);\coordinate (se) at ($(ne0) + (0, -\h)$); \filldraw [-, line width = \lwidth, fill=#4] (nw) -- (ne1) -- (ne2)[rounded corners=\cornerradius]--(se) -- (nw|-se) -- cycle;\draw [-, line width = \lwidth] (ne1) [rounded corners=\cornerradius]-- (ne1|-ne2) -- (ne2);\node [anchor=north west,TREE] at (nw) {#2};\foreach \k in {0,...,\nline}{\draw [-, line width = \lwidth, line cap=round] ($(nw|-se) + (\iconmargin,\iconmargin) + (0,{(\k-1)/(\nline-1)*(\h - \iconmargin - \topmargin)})$)-- ++ ($(\w,0) - 2*(\iconmargin,0)$);}\end{tikzpicture}};}% end of file definition\begin{tikzpicture}\coordinate (G_ul) at (0,0);\coordinate (G_ll) at ($(G_ul) +(0,-10)$);\coordinate (G_lr) at ($(G_ll) +(4,0)$);\coordinate (G_ur) at ($(G_ul) +(4,0)$);\node[TREE,anchor=west] at ($(G_ul) +(0,-0.5)$) (git) {.git/};\file{$(git.west) +(1.5,-2)$}{HEAD}{HEAD}{color_head}\file{$(HEAD) +(0,-2)$}{index}{index}{color_index}\draw[] (G_ul) -- (G_ll) -- (G_lr) -- (G_ur) -- cycle;%refs\node[TREE,anchor=west] at ($(G_ul) +(-5,-0.5)$) (git_ref) {.git/refs/};\file{$(git_ref.west) +(1.5,-2)$}{\textcolor{white}{master}}{master}{color_master}\draw[] ($(G_ul) +(-5,0)$) -- ++(0,-10) -- ++(4.9,0) -- ++(0,10) -- cycle;%files\node[TREE,anchor=west] at ($(G_ul) +(-21,-0.5)$) (root) {/};\file{$(root.west) +(1.7,-2)$}{\textcolor{black}{file1}}{file1}{color_file}\node[TREE,anchor=west] at ($(root |- file1) +(0.4,-2.2)$) (dir1) {dir1/};\draw [very thick] (root) -- (root |- file1) -- ++(0.2,0);\draw [very thick] (root) -- (root |- dir1) -- ++(0.2,0);\file{$(dir1.west) +(1.7,-1.0)$}{\textcolor{black}{file2}}{file2}{color_file}\draw [very thick] ($(dir1.west) +(0.2,-0.2)$) -- ($(dir1.west |- file2) +(0.2,0)$) -- ++(0.2,0);\file{$(file2.west) +(0,-1.5)$}{\textcolor{black!70}{f.tmp}}{file3}{color_file}\draw [very thick] ($(dir1.west) +(0.2,-0.2)$) -- ($(dir1.west |- file3) +(0.2,0)$) -- ++(0.2,0);\file{$(root.west) +(1.7,-8.5)$}{\textcolor{black}{.giti.}}{file4}{color_file}\draw [very thick] (root) -- (root |- file4) -- ++(0.2,0);\begin{pgfonlayer}{background}\draw[fill=color_workspace!20] ($(G_ul) +(-21,0)$) -- ++(0,-10) -- ++(3.9,0) -- ++(0,10) -- cycle;\end{pgfonlayer}%objects\node[TREE,anchor=west] at ($(G_ul) +(-17,-0.5)$) (git_object) {.git/objects/};\file{$(git_object.west) +(1.5,-2)$}{\textcolor{black}{hashblob1}}{blob1}{color_workspace!20}\file{$(git_object.west) +(1.5,-3.5)$}{\textcolor{black}{hashblob3}}{blob2}{color_workspace}\file{$(git_object.west |- file2.north) +(1.5,-0)$}{\textcolor{black}{hashblob2}}{blob3}{color_workspace!20}\file{$(git_object.west |- file3.north) +(1.5,0)$}{\textcolor{black}{hashblob5}}{blob4}{color_workspace}\file{$(git_object.west |- file4.north) +(1.5,0)$}{\textcolor{black}{hashblob4}}{blob5}{color_workspace}\draw[] ($(G_ul) +(-17,0)$) -- ++(0,-10) -- ++(11.9,0) -- ++(0,10) -- cycle;%trees\file{$(blob1.west) +(5,0)$}{\textcolor{black}{hashtree1}}{tree1}{color_tree!20}\file{$($(blob3.west) !0.5! (blob3.west)$) +(3,0)$}{\textcolor{black}{hashtree3}}{tree2}{color_tree!20}\file{$($(blob2.west) !0.5! (blob3.west)$) +(5.75,0)$}{\textcolor{black}{hashtree2}}{tree3}{color_tree!20}\file{$($(blob4.west) !0.5! (blob4.west)$) +(3,0)$}{\textcolor{black}{hashtree5}}{tree4}{color_tree}\file{$($(blob4.west) !0.5! (blob5.west)$) +(5.75,0)$}{\textcolor{black}{hashtree4}}{tree5}{color_tree}%commits\file{$(tree1.west) +(3,0)$}{\textcolor{black}{hashcommits1}}{commit1}{color_commit!20}\file{$(tree3.west) +(2.75,0)$}{\textcolor{black}{hashcommits2}}{commit2}{color_commit!20}\file{$(tree5.west) +(2.75,0)$}{\textcolor{black}{hashcommits3}}{commit3}{color_commit}%arrows\draw[->,very thick,draw=black] ($(HEAD) +(-1,0)$) -- ($(master) +(1.2,0)$);\draw[->,very thick] ($(master) +(-1,0)$) -- ($(commit3) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(commit1) +(-1,0)$) -- ($(tree1) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(tree1) +(-1,0)$) -- ($(blob1) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(commit2) +(-1,0)$) -- ($(tree3) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(tree3) +(-1,0)$) -- ($(tree2) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(tree3) +(-1,0)$) -- ($(blob2) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(tree2) +(-1,0)$) -- ($(blob3) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(commit2) +(0,0.6)$) -- ($(commit1) +(0,-0.7)$);\draw[->,very thick] ($(commit3) +(-1,0)$) -- ($(tree5) +(1.2,0)$);\draw[->,very thick] ($(tree5) +(-1,0)$) -- ($(tree4) +(1.2,0)$);\draw[->,very thick] ($(tree5) +(-1,0)$) -- ($(blob5) +(1.2,0)$);\draw[->,very thick] ($(tree4) +(-1,0)$) -- ($(blob4) +(1.2,0)$);\draw[->,very thick] ($(tree5) +(-1,0)$) -- ($(blob2) +(1.2,0)$);\draw[->,very thick] ($(commit3) +(0,0.6)$) -- ($(commit2) +(0,-0.7)$);\draw[<-,very thick,dashed] ($(blob2) +(-1.2,0)$) -- ($(file1) +(1.2,0)$);\draw[<-,very thick,dashed] ($(blob4) +(-1.2,0)$) -- ($(file2) +(1.2,0)$);\draw[<-,very thick,dashed] ($(blob5) +(-1.2,0)$) -- ($(file4) +(1.2,0)$);\draw[->,very thick] ($(index) +(-1,0)$) -- ($(blob3) +(1.2,0)$);\draw[->,very thick] ($(index) +(-1,0)$) -- ($(blob2) +(1.2,0)$);\end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)#commit hash = scan('~/tmp/Repo1/.git/refs/heads/main', what='character')hash_commits = c(hash_commits, strtrim(hash, width = 5))# treehash = system(paste0("cd ~/tmp/Repo1; git cat-file -p ", hash_commits[2]), intern = TRUE)hash = read.table(textConnection(hash[1]))hash_tree = c(hash_tree, strtrim(hash$V2, 5))hash = system(paste0('cd ~/tmp/Repo1; git cat-file -p HEAD^{tree}'), intern=TRUE)hash = read.table(textConnection(hash))hash_tree = c(hash_tree, strtrim(hash$V3[hash$V2 == "tree"], 5))#blobhash = system('cd ~/tmp/Repo1; git ls-files --stage', intern=TRUE)hash = read.table(textConnection(hash))hash_blob = unique(c(hash_blob,strtrim(hash$V2,5)))system(paste0("sed -i 's/hashblob1/", hash_blob[1], "/g' ../resources/Fig-advanced6.tikz"))system(paste0("sed -i 's/hashblob2/", hash_blob[2], "/g' ../resources/Fig-advanced6.tikz"))system(paste0("sed -i 's/hashblob3/", hash_blob[3], "/g' ../resources/Fig-advanced6.tikz"))system(paste0("sed -i 's/hashblob4/", hash_blob[4], "/g' ../resources/Fig-advanced6.tikz"))system(paste0("sed -i 's/hashblob5/", hash_blob[5], "/g' ../resources/Fig-advanced6.tikz"))system(paste0("sed -i 's/hashtree1/", hash_tree[1], "/g' ../resources/Fig-advanced6.tikz"))system(paste0("sed -i 's/hashtree2/", hash_tree[2], "/g' ../resources/Fig-advanced6.tikz"))system(paste0("sed -i 's/hashtree3/", hash_tree[3], "/g' ../resources/Fig-advanced6.tikz"))system(paste0("sed -i 's/hashtree4/", hash_tree[4], "/g' ../resources/Fig-advanced6.tikz"))system(paste0("sed -i 's/hashtree5/", hash_tree[5], "/g' ../resources/Fig-advanced6.tikz"))system(paste0("sed -i 's/hashcommits1/", hash_commits[1], "/g' ../resources/Fig-advanced6.tikz"))system(paste0("sed -i 's/hashcommits2/", hash_commits[2], "/g' ../resources/Fig-advanced6.tikz"))system(paste0("sed -i 's/hashcommits3/", hash_commits[3], "/g' ../resources/Fig-advanced6.tikz"))system("xelatex -output-directory=../resources ../resources/Fig-advanced6.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig-advanced6.pdf ../resources/Fig-advanced6.png")```![](../resources/Fig-advanced6.png)::: {.callout-note collapse="true"}## Another visual representation of the git```{bash}#| label: gitDraw6#| engine: bash#| cache: false#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit6.png ```![](10_git_files/figure-html/drawGit6.png){width=100%}:::::: {.callout-note collapse="true"}## More information about committing changes to the repositoryCommitting staged changes creates an object under the `.git` tree.```{r, replace=TRUE}#| label: git-add6a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -a --charset unicode``````{r, replace=TRUE}#| label: git-add6b#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git cat-file -p HEAD``````{r, replace=TRUE}#| label: git-add6c#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git cat-file -p HEAD^{tree}``````{r, replace=TRUE}#| label: git-add6d#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --oneline```:::# Inspecting a repositoryFor this section, will will be working on the repository built up inthe previous section. If you did not follow along with the previoussection, I suggest that you expand the following callout and run theprovided code in a terminal.If you already have the repository, you can ignore the commands tocreate the repository.::: {.callout-note collapse="true"}## Commands to create the the repositoryIssue the following commands in your terminal```{bash}#| label: git_status1#| classes: bash#| engine: bash#| echo: true#| eval: falserm-rf ~/tmp/Repo1mkdir ~/tmp/Repo1cd ~/tmp/Repo1git init echo'File 1'> file1git add file1git commit -m'Initial repo and added file1'echo'---------------'>> file1mkdir dir1echo'* Notes'> dir1/file2git add file1 dir1/file2git commit -m'Modified file1 and added file2 (in dir1)'echo'---'>> dir1/file2echo'temp'> dir1/f.tmpecho'*.tmp'> .gitignoregit add .git commit -m'Modified file2, added .gitignore'``````{bash}#| label: git-status2a#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1tree-ra-L 2 --charset ascii```:::## Status of workspace and staging areaRecall that within the .git environment, files can be in one of fourstates:- untracked- modified- staged- committedTo inspect the status of files in your workspace, you can issue the`git status` <i>command</i> (as we have done on numerous occasionsabove). This command displays the current state of the workspace andstaging area.::: {.panel-tabset}### Terminal```{bash}#| label: git_status2#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git status```The output of `git status` partitions all the files into (staged:`Changes to be committed`, unstaged: `Changes not staged for commit`and `Untracked`) as well as hints on how to either promote or demotethe status of these files.### RstudioExamine the git status panel - ideally it should be empty therebysignalling that all your important files are tracked andcommitted.:::### log of commits::: {.panel-tabset}#### TerminalThe `git log` _command_ allows us to review the history of committedsnapshots```{bash}#| label: git_log#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git log```We can see that in my case some fool called 'Murray Logan' has made atotal of three commits. We can also see the date/time that the commitswere made as well as the supplied commit comment.Over time repositories accumulate a large number of commits, to onlyreview the last 2 commits, we could issue the `git log -n2` _command_. ```{bash}#| label: git_log1#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git log -n 2 ```::: {.callout-note collapse="true"}## Additional useful (`git log`) options<table><thead><th>Option</th><th>Example</th></thead><tbody><tr><td>`--oneline`<br>Condensed view</td><td>```{bash}#| label: git_log2a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git log --oneline```</td></tr><tr><td>`--stat`<br>Indicates number of changes</td><td>```{bash}#| label: git_log2b#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git log --stat```</td></tr><tr><td>`-p`<br>Displays the full diff of each commit</td><td>```{bash}#| label: git_log2c#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git log -p```</td></tr><tr><td>`--author="<name>"`<br>Filter by author</td><td>```{bash}#| label: git_log2d#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git log --author="Murray"```</td></tr><tr><td>`--grep="<pattern>"`<br>Filter by regex pattern of commit message</td><td>```{bash}#| label: git_log2e#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git log --grep="Modified"```</td></tr><tr><td>`<file>`<br>Filter by filename</td><td>```{bash}#| label: git_log2f#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git log file1```</td></tr><tr><td>`--decorate --graph`</td><td>```{bash}#| label: git_log2g#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git log --graph--decorate--oneline```</td></tr><tr><td>`--all`<br>All branches</td><td>```{bash}#| label: git_log2h#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git log --all```</td></tr></tbody></table>:::#### RstudioTo explore the history of a repository, click on the clock icon ("Viewhistory of previous commits" button). This will open up the "ReviewChanges" window in the "History" tab.![](../resources/rstudio_gitstatus1.png)Along with the reverse chronological list of commits, for each commit(and file thereof), you can explore the changes (diffs) that occurred.Text that appears over a green background represents text that havebeen added as part of the current commit. Text that appears over a redbackground represents text that have been removed.If we scroll down and explore the changes in `dir1/file2` for the mostrecent commit, we see that the text `* Notes` was removed and then `*Notes` and `---` were added. At first this might seem a bit odd - whywas `* Notes` deleted and then added back? ![](../resources/rstudio_gitstatus2.png)Git works on entire lines of text. So the first line was replacedbecause in the newer version, the first line had a carriage return(newline character). Although we cant see this character, it isthere - we see it more via its effect (sending the text after it tothe next line). Hence, in fact, two lines of text were actuallychanged in the most recent commit.:::### `reflog`::: {.panel-tabset}#### TerminalAnother way to explore the commit history is to look at the**reflog**. This is a log of the _branch_ references. This approach ismore useful when we have multiple _branches_ and so will be visited inthe <a href="#Branching">section on branching</a>. It displays allrepository activity, not just the commits.```{bash}#| label: git_reflog1#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git reflog```#### RstudioSome of this sort of information can be gleaned from the git"History". Just make sure you select ("all branches") from the "Switchbranch" menu.![](../resources/rstudio_gitstatus3.png):::### `diff`Whilst some of these actions described in this section are availablefrom the "History" tab of the "Review Changes" window in Rstudio, mostare only available as terminal commands.```{tikz}%| label: Fig-advancedDiff1%| engine: tikz%| echo: false%| cache: true%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \usetikzlibrary{arrows.meta}\tikzstyle{TARGET} = [font={\fontspec[Scale=2]{NotoSans-Regular}}] \tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}] \tikzstyle{TREE} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}] \tikzstyle{fileText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] \definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_index}{rgb}{0.78,0.86,0.27}\definecolor{color_local}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\definecolor{color_master}{rgb}{0.36,0.27,0.87}\definecolor{color_head}{HTML}{6495ED} %\definecolor{color_head}{HTML}{6495ED}% A template for making the storage symbol\newcommand{\state}[3]{\draw (#1) node [draw=none,fill=#2,shape=circle,minimum width=2cm] (#3) {\begin{tikzpicture}\node [draw=white, fill=white,shape=cylinder,shape aspect=1.3 ,shape border rotate=90,minimum height=1.6cm,minimum width=1.5cm] at (0,0) (Cylinder) {}; \draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.1cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.5cm)$) arc (-180:0:0.8cm and 0.2cm); \draw[draw=#2,very thick,line width=0.1cm,anchor=north west] ($(Cylinder.north west) +(-0.05cm,-0.9cm)$) arc (-180:0:0.8cm and 0.2cm); \end{tikzpicture}};}% Define dirtree\makeatletter\newcount\dirtree@lvl\newcount\dirtree@plvl\newcount\dirtree@clvl\def\dirtree@growth{%\ifnum\tikznumberofcurrentchild=1\relax\global\advance\dirtree@plvl by 1\expandafter\xdef\csname dirtree@p@\the\dirtree@plvl\endcsname{\the\dirtree@lvl}\fi\global\advance\dirtree@lvl by 1\relax\dirtree@clvl=\dirtree@lvl\advance\dirtree@clvl by -\csname dirtree@p@\the\dirtree@plvl\endcsname\pgf@xa=0.25cm\relax\pgf@ya=-0.5cm\relax\pgf@ya=\dirtree@clvl\pgf@ya\pgftransformshift{\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%\ifnum\tikznumberofcurrentchild=\tikznumberofchildren\global\advance\dirtree@plvl by -1\fi}\tikzset{dirtree/.style={growth function=\dirtree@growth,every node/.style={anchor=north},every child node/.style={anchor=west},edge from parent path={(\tikzparentnode\tikzparentanchor) |- (\tikzchildnode\tikzchildanchor)}}}\makeatother%end of t%Define a file \newcommand{\file}[3] {\coordinate (#3) at (#1);\draw [anchor=top left](#1) rectangle ++(2,-2);\draw [fill=color_workspace]($(#1) +(2,0)$) -- ++(0,-0.5) --++(-0.5,0.5) -- cycle; \node [anchor=north west,fileText] at ($(#1) +(0.3,-0.3)$) {#2};}% end of file definition\begin{tikzpicture}\state{0,0}{color_workspace}{W}\node[TARGET,fill=white] at ($(W.north) +(0,0.5cm)$) {Workspace};\draw [line width=0.1cm,draw=color_workspace] (W.south) -- ++(0,-5); \state{8,0}{color_index}{I}\node[TARGET,fill=white] at ($(I.north) +(0,0.5cm)$) {Staging Area};\draw [line width=0.1cm,draw=color_index] (I.south) -- ++(0,-5); \state{16,0}{color_local}{L}\node[TARGET,fill=white] at ($(L.north) +(0,0.5cm)$) {Local Repository};\draw [line width=0.1cm,draw=color_local] (L.south) -- ++(0,-5); \draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_index] ($(W) + (0.1cm,-3)$) -- ($(I) + (-0.1cm,-3)$) node[anchor=west,pos=0, text=black,align=center,CODE] {git diff};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_local] ($(I) + (0.1cm,-3)$) -- ($(L) + (-0.1cm,-3)$) node[anchor=west,pos=0, text=black,align=center,CODE] {git diff --cached};\draw[{Round Cap[length=0.5em]}-Triangle Cap,very thick, line width=1cm, draw=color_local] ($(W) + (0.1cm,-5)$) -- ($(L) + (-0.1cm,-5)$) node[anchor=west,pos=0, text=black,align=center,CODE] {git diff HEAD};\end{tikzpicture}```Two of the three commits in our repository involved modifications to afile (`dir1/file2`). To further help illustrate commands to comparefiles indifferent states, we will additionally make a further changeto `dir1/file2`. The `git diff` allows us to explore differencesbetween:- the workspace and the staging area (index)```{bash} #| label: git_diff1 #| echo: !expr -1 #| eval: true #| cache: false #| classes: bash #| engine: bash cd ~/tmp/Repo1 # lets modify dir1/file2 echo 'Notes' >> dir1/file2 git diff ``` The output indicates that we are comparing the blob representing`dir1/file2` in the <i>index</i> (staging area) with the newly modified `dir1/file2`. The next couple of rows indicate that the <i>indexed</i> version will be represented by a '-' sign and the new version will be represented by a '+' sign. The next row (which is surrounded in a pair of `@` signs, indicates that there are two lines that have changed. Finally the next two rows show that a charrage return has been added to the end of the first line and the new version has added the word 'Notes' to the next line.- the staging area and the last commit```{bash} #| label: git_diff3 #| echo: !expr -1 #| eval: true #| cache: false #| classes: bash #| engine: bash cd ~/tmp/Repo1 git add . git diff --cached ``` Once we stage the modifications, we see that the same differences are recorded.- the index and a tree (in this case, the current tree)```{bash} #| label: git_diff4 #| echo: !expr -1 #| eval: true #| cache: false #| classes: bash #| engine: bash cd ~/tmp/Repo1 git diff --cached HEAD^{tree} ```- the workspace and the current commit```{bash} #| label: git_diff5 #| echo: !expr -1 #| eval: true #| cache: false #| classes: bash #| engine: bash cd ~/tmp/Repo1 git diff HEAD ```- two commits (e.g. previous and current commits)```{bash} #| label: git_diff6 #| echo: !expr -1 #| eval: true #| cache: false #| classes: bash #| engine: bash cd ~/tmp/Repo1 git diff HEAD^ HEAD ```- two trees (first example, the current and previous commit trees)```{bash} #| label: git_diff7 #| echo: !expr -1 #| eval: true #| cache: false #| classes: bash #| engine: bash cd ~/tmp/Repo1 git diff HEAD^{tree} HEAD^^{tree} ``````{bash} #| label: git_diff8 #| echo: !expr -1 #| eval: true #| cache: false #| classes: bash #| engine: bash cd ~/tmp/Repo1 git diff 07a94 2b61e ```- two blobs (indeed any two objects)```{bash} #| label: git_diff9 #| echo: !expr -1 #| eval: true #| cache: false #| classes: bash #| engine: bash cd ~/tmp/Repo1 git diff 50fcd 28ed2 ```### `ls-files`Similar to the previous section, the following is only reallyavailable via the terminal.We can list the files that comprise the repository by:```{bash}#| label: git_ls_files#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git ls-files ```The change to `dir1/file2` above was just to illustrate the `gitdiff`. In doing so we now have a modified version of this file thathas not been committed Before we move on, I am going to remove thesechanges so that the `dir1/file2` is not in a modified state andreflects the state of the current commit. To do so, I will use performa **hard reset** (`git reset --hard`). More will be discussed aboutthe `git reset` command later in this tutorial - for now all that isimportant is to know that it restores the workspace to a previousstate.In addition to the `git reset --hard`, I will also clean and prune the repository.```{bash}#| label: git_ls_files1#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git reset --hardgit clean -qfdxgit reflog expire --expire-unreachable=now --allgit gc --prune=now```## TagsAlthough it is possible to track the history of a repository via itscommit sha1 names, most find it more convenient to apply **tags** tocertain milestone commits. For example, a particular commit mightrepresent a specific point in the history of a project - such as arelease version. Git tags allow us to apply more human readable flags.::: {.panel-tabset}#### Terminal```{bash}#| label: git_tag1a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git tag V.1```In the above, `V.1` is the tag we are applying to the most recentcommit. For this example, `V.1` simply denotes something like "version1". The tag must not contain any spaces (just replace space characterswith underscores or periods).```{bash}#| label: git_tag1b#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git log --graph--decorate--oneline``````{bash}#| label: git_tag1c#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git reflog```#### RstudioThe functionality to add tags to commits is not directly supported inRstudio. in order to apply a tag, you will need to switch the terminaland enter a command like:![](../resources/rstudio_gittag1.png)```{bash}#| label: git_tag1a#| echo: !expr -1#| eval: false#| cache: false#| classes: bash#| engine: bash```In the above, `V.1` is the tag we are applying to the most recentcommit. For this example, `V.1` simply denotes something like "version1". The tag must not contain any spaces (just replace space characterswith underscores or periods).Now if we return to the "History" tab of the "Review Changes" window,we can see the tag represented by a yellow tag in the commit diagram.![](../resources/rstudio_gittag2.png):::::: {.callout-note collapse="true"}## Another visual representation of the repository```{bash}#| label: gitDraw7#| engine: bash#| cache: false#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit7.png ```![](10_git_files/figure-html/drawGit7.png){width=100%}:::# BranchingAgain we will start with our repository. For this section, will beworking on the repository built up in the previous section.::: {.callout-note collapse="true"}## Commands to create the repository (from above)```{bash}#| label: git_status1a#| classes: bash#| engine: bash#| echo: true#| cache: false#| eval: truerm-rf ~/tmp/Repo1mkdir ~/tmp/Repo1cd ~/tmp/Repo1git init echo'File 1'> file1git add file1git commit -m'Initial repo and added file1'echo'---------------'>> file1mkdir dir1echo'* Notes'> dir1/file2git add file1 dir1/file2git commit -m'Modified file1 and added file2 (in dir1)'echo'---'>> dir1/file2echo'temp'> dir1/f.tmpecho'*.tmp'> .gitignoregit add .git commit -m'Modified file2, added .gitignore'git tag V.1```:::::: {.callout-note collapse="true"}## Graphical representations of the repositoryThe following are a couple of different visuals of the repository.```{bash}#| label: git-status2a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash```![](../resources/Fig-advanced6.png)```{bash}#| label: gitDraw6a#| engine: bash#| cache: false#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit6a.png ```![](10_git_files/figure-html/drawGit6a.png){width=100%}:::```{cat}#| label: Fig7#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig7.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_commit}{commitshash3}{commitscomment3}\master{below = 1cm of commitshash3} \HEAD{below = 0.1cm of master} \draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);\end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))# get the commits from .git/logs/HEADsystem(paste0("sed -i 's/commitshash1/", commits$hash[1], "/g' ../resources/Fig7.tikz"))system(paste0("sed -i 's/commitscomment1/", commits$comment[1], "/g' ../resources/Fig7.tikz"))system(paste0("sed -i 's/commitshash2/", commits$hash[2], "/g' ../resources/Fig7.tikz"))system(paste0("sed -i 's/commitscomment2/", commits$comment[2], "/g' ../resources/Fig7.tikz"))system(paste0("sed -i 's/commitshash3/", commits$hash[3], "/g' ../resources/Fig7.tikz"))system(paste0("sed -i 's/commitscomment3/", commits$comment[3], "/g' ../resources/Fig7.tikz"))system("xelatex -output-directory=../resources ../resources/Fig7.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig7.pdf ../resources/Fig7.png")```![](../resources/Fig7.png)Lets assume that the current commit represents a largely stableproject. We are about to embark on a substantial modification in theform of a new feature that will involve editing `file1` and adding anew file to `dir1`. At the same time, we wish to leave open thepossibility of committing additional minor changes to the currentcommit in order to address any bugs or issues that might arise.In essence what we want to do is start a new _branch_ for the newfeature. This is performed in two steps:1. use the `git branch <name>` _command_ to generate a new branch _reference_. In the following example, I will call the new branch **Feature**, but it can be anything.:::: {.indented}::: {.panel-tabset}## Terminal```{bash}#| label: git_branch1#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git branch Feature```## RstudioTo create a new branch, click on the "New branch" icon (see figurebelow) and enter a name (e.g. `Feature`) for the branch in the popupbox.![](../resources/rstudio_gitbranch1.png):::```{cat}#| label: Fig7a#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig7a.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz} \usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\tikzstyle{refText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] \begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_commit}{commitshash3}{commitscomment3}\master{below = 1cm of commitshash3} \HEAD{below = 0.1cm of master} \draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);\branch{below = 0.1cm of HEAD}{Feature} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))# get the commits from .git/logs/HEADsystem(paste0("sed -i 's/commitshash1/", commits$hash[1], "/g' ../resources/Fig7a.tikz"))system(paste0("sed -i 's/commitscomment1/", commits$comment[1], "/g' ../resources/Fig7a.tikz"))system(paste0("sed -i 's/commitshash2/", commits$hash[2], "/g' ../resources/Fig7a.tikz"))system(paste0("sed -i 's/commitscomment2/", commits$comment[2], "/g' ../resources/Fig7a.tikz"))system(paste0("sed -i 's/commitshash3/", commits$hash[3], "/g' ../resources/Fig7a.tikz"))system(paste0("sed -i 's/commitscomment3/", commits$comment[3], "/g' ../resources/Fig7a.tikz"))system("xelatex -output-directory=../resources ../resources/Fig7a.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig7a.pdf ../resources/Fig7a.png")```![](../resources/Fig7a.png)::: {.callout-note}Note, at this stage we have only created a reference to a new branch,until we commit to this branch, its contents will be the same as themain branch.:::::: {.callout-note collapse="true"}## More information following the creation of the branch```{r, replace=TRUE}#| label: git-branch7a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline``````{r, replace=TRUE}#| label: git-branch7a2#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset asciigit reflog```:::::: {.callout-note collapse="true"}## Another visual representation of the repository```{bash}#| label: gitDraw7a#| engine: bash#| cache: false#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit7a.png ```![](10_git_files/figure-html/drawGit7a.png){width=100%}:::::::2. use the `git checkout <name>` _command_ to move the `HEAD` to the tip of this new branch (`Feature`).:::: {.indented}::: {.panel-tabset}## Terminal```{bash}#| label: git_branch2#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git checkout Feature```## RstudioTo checkout a branch in Rstudio, click on the "Switch branch" buttonand select the branch name (in this case `Feature`) from the dialog box.![](../resources/rstudio_gitbranch3.png)If we now examine the "History" tab of the "Review Changes" window, wewill see that the most recent commit has both `main` and `Feature`branch markers. However, the main connection is with the `Feature`branch indicating that the `HEAD` is currently on the `Feature`branch.![](../resources/rstudio_gitbranch2.png):::```{cat}#| label: Fig7b#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig7b.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz} \usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_commit}{commitshash3}{commitscomment3}\master{below = 1cm of commitshash3} \draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);\branch{below = 0.1cm of master}{Feature} \HEAD{below = 0.1cm of Feature} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))# get the commits from .git/logs/HEADsystem(paste0("sed -i 's/commitshash1/", commits$hash[1], "/g' ../resources/Fig7b.tikz"))system(paste0("sed -i 's/commitscomment1/", commits$comment[1], "/g' ../resources/Fig7b.tikz"))system(paste0("sed -i 's/commitshash2/", commits$hash[2], "/g' ../resources/Fig7b.tikz"))system(paste0("sed -i 's/commitscomment2/", commits$comment[2], "/g' ../resources/Fig7b.tikz"))system(paste0("sed -i 's/commitshash3/", commits$hash[3], "/g' ../resources/Fig7b.tikz"))system(paste0("sed -i 's/commitscomment3/", commits$comment[3], "/g' ../resources/Fig7b.tikz"))system("xelatex -output-directory=../resources ../resources/Fig7b.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig7b.pdf ../resources/Fig7b.png")```![](../resources/Fig7b.png)The only noticable change is that we are now considered to be on the"Feature" branch (notice that HEAD is pointing to Feature). Any newcommits will be applied to this new branch.::: {.callout-note collapse="true"}## More information following the creation of the branch```{r, replace=TRUE}#| label: git-branch7b#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline``````{r, replace=TRUE}#| label: git-branch7b2#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset asciigit reflog```:::::: {.callout-note collapse="true"}## Another visual representation of the repository```{bash}#| label: gitDraw7b#| engine: bash#| cache: false#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit7b.png ```![](10_git_files/figure-html/drawGit7b.png){width=100%}:::::::Now if we make and commit a change (such as an edit to `file1` and anaddition of `file3` within `dir1`), we will be operating on a separate_branch_.::: {.panel-tabset}## Terminal```{bash}#| label: git_branch3#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1echo'b'>> file1echo'File 3'> dir1/file3git add .git commit -m'New feature'```## Rstudio1. modify `file1` by adding a carriage return followed by some additional text (e.g. a `b`) like in the figure below![](../resources/rstudio_gitbranch4.png)2. navigate to this new directory (`dir1`)3. click the "Create a new blank file in current directory" button and select "Text file"4. enter a new filename (`file3`) into the popup box5. enter some text into this file (such as `File 3`)![](../resources/rstudio_gitbranch5.png)6. stage (add) the two modified files using their respective checkboxes7. click the "Commit" button, provide a commit message (sch as "New feature")8. close the popup9. goto the "History" tab![](../resources/rstudio_gitbranch6.png)Notice that we have advanced one commit on this new branch.:::```{cat}#| label: Fig7c#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig7c.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz} \usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);\master{below = 1cm of commitshash3} \commit{right = 1.5cm of commitshash3}{commitshash5}{color_commit}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (commitshash3.east) -- (commitshash5.west);\branch{below = 1cm of commitshash5}{Feature} \HEAD{below = 0.1cm of Feature} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig7c.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig7c.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig7c.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig7c.pdf ../resources/Fig7c.png")```![](../resources/Fig7c.png)::: {.callout-note collapse="true"}## More information following the creation of the branch```{r, replace=TRUE}#| label: git-branch7c#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline``````{r, replace=TRUE}#| label: git-branch7c2#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset asciigit reflog```:::::: {.callout-note collapse="true"}## Another visual representation of the repository```{bash}#| label: gitDraw7c#| engine: bash#| cache: false#| echo: false#| eval: true./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit7c.png ```![](10_git_files/figure-html/drawGit7c.png){width=100%}:::So we can now continue to develop the `Feature` _branch_. But what ifwe now decided that we wanted to make a change to the `main`_branch_ (perhaps addressing a bug or issue).1. switch over to the `main` branch:::: {.indented}::: {.panel-tabset}## Terminal```{bash}#| label: git_branch4#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git checkout main```## RstudioUse the "Switch branch" selector to checkout the `main` branch:::```{cat}#| label: Fig7d#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig7d.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz} \usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_commit}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);\master{below = 1cm of commitshash3} \HEAD{below = 0.1cm of master} \commit{right = 1.5cm of commitshash3}{commitshash5}{color_inactive}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (commitshash3.east) -- (commitshash5.west);\branch{below = 1cm of commitshash5}{Feature} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig7d.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig7d.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig7d.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig7d.pdf ../resources/Fig7d.png")```![](../resources/Fig7d.png)::: {.callout-note collapse="true"}## More information following the creation of the branch```{r, replace=TRUE}#| label: git-branch7d#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all``````{r, replace=TRUE}#| label: git-branch7d2#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset ascii``````{r, replace=TRUE}#| label: git-branch7d3#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reflog```:::::: {.callout-note collapse="true"}## Another visual representation of the repository```{bash}#| label: gitDraw7d#| engine: bash#| cache: false#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit7d.png ```![](10_git_files/figure-html/drawGit7d.png){width=100%}:::::::2. make the necessary changes to the files and commit them on the`main` _branch_:::: {.indented}::: {.panel-tabset}## Terminal```{bash}#| label: git_branch5#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1echo' another bug fix'>> dir1/file2git add .git commit -m'Bug fix in file1'```## Rstudio1. make the following edits to `dir1/file2`![](../resources/rstudio_gitbranch7b.png)2. stage and commit the changes:::```{cat}#| label: Fig7e#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig7e.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz} \usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);\commit{right = 1.5cm of commitshash3}{commitshash7}{color_commit}{commitshash7}{commitscomment7}\draw [<-,line width=3pt,draw=black!60] (commitshash3.east) -- (commitshash7.west);\commit{above = 3cm of commitshash7}{commitshash5}{color_inactive}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (commitshash3.north east) -- (commitshash5.south west);\branch{below = 1cm of commitshash5}{Feature} \master{below = 1cm of commitshash7} \HEAD{below = 0.1cm of master} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig7e.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig7e.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig7e.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig7e.pdf ../resources/Fig7e.png")```![](../resources/Fig7e.png)::: {.callout-note collapse="true"}## More information following the creation of the branch```{r, replace=TRUE}#| label: git-branch7e#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all``````{r, replace=TRUE}#| label: git-branch7e2#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset asciigit reflog```:::::: {.callout-note collapse="true"}## Another visual representation of the repository```{bash}#| label: gitDraw7e#| engine: bash#| cache: false#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit7e.png ```![](10_git_files/figure-html/drawGit7e.png){width=100%}:::::::We could simultaneously make additional modifications to the `Feature`_branch_ just by simply checking out the `Feature` _branch_ andcommiting those modifications. To illustrate, we will make anotherchange to the `dir1/file3` file.::: {.callout-info collapse="false"}For this demonstration we are deliberately avoiding making edits toeither `file1` or `dir1/file2`. This is because if we did, there is achance that we might introduce conflicting edits of the same lines offiles across the two branches (`main` and `Feature`).In a later section, we WILL deliberately introduce a conflict so thatwe can see how to resolve conflicts.:::::: {.panel-tabset}## Terminal```{bash}#| label: git_branch6#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git checkout Featureecho' a modification'>> dir1/file3git add .git commit -m'Feature complete'```## Rstudio1. checkout the `Feature` branch using the "Switch branch" selector2. modify `dir1/file3` by adding a carriage return followed by some additional text (e.g. ` a modification`) like in the figure below![](../resources/rstudio_gitbranch7c.png)3. stage (add) this file4. commit the change with the message of `Feature complete`:::```{cat}#| label: Fig7f#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig7f.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz} \usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);\commit{right = 1.5cm of commitshash3}{commitshash7}{color_commit}{commitshash7}{commitscomment7}\draw [<-,line width=3pt,draw=black!60] (commitshash3.east) -- (commitshash7.west);\commit{above = 3cm of commitshash7}{commitshash5}{color_inactive}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (commitshash3.north east) -- (commitshash5.south west);\master{below = 1cm of commitshash7} \commit{right = 1.5cm of commitshash5}{commitshash9}{color_commit}{commitshash9}{commitscomment9}\draw [<-,line width=3pt,draw=black!60] (commitshash5.east) -- (commitshash9.west);\branch{below = 1cm of commitshash9}{Feature} \HEAD{below = 0.1cm of Feature} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig7f.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig7f.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig7f.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig7f.pdf ../resources/Fig7f.png")```![](../resources/Fig7f.png)::: {.callout-note collapse="true"}## More information following the creation of the branch```{r, replace=TRUE}#| label: git-branch7f#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --allgit reflog``````{r, replace=TRUE}#| label: git-branch7f2#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset ascii```:::::: {.callout-note collapse="true"}## Another visual representation of the repository```{bash}#| label: gitDraw7f#| engine: bash#| cache: false#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit7f.png ```![](10_git_files/figure-html/drawGit7f.png){width=100%}:::## Merge branchesFinally, (if we are satisfied that `Feature` is stable and complete),we might like to introduce these changes into the `main` _branch_ sothat they become a part of the main project base. This operation iscalled a **merge** and is completed with the `git merge <branch>`_command_ where `<branch>` is the name of the _branch_ you want tomerge the current branch (that pointed to by `HEAD`) with. Typicallywe want to _merge_ the _non-main branch_ with the `main` _branch_.Therefore we must be _checkout_ the `main` _branch_ before _merging_.::: {.panel-tabset}If we add the _switch_ `--no-edit`, then rather than supply a commitmessage (or be dropped into an editor to make a commit message), thedefault merge message will be applied to the commit.## Terminal```{bash}#| label: git_branch7a#| echo: !expr -1#| eval: true#| cache: false#| error: true#| message: true#| classes: bash#| engine: bashcd ~/tmp/Repo1git checkout main``````{bash}#| label: git_branch7b#| echo: !expr -1#| eval: true#| cache: false#| error: true#| message: true#| classes: bash#| engine: bashcd ~/tmp/Repo1git merge Feature --no-edit```## Rstudio1. checkout the `main` branch using the "Switch branch" selector2. merging is not directly supported in Rstudio, so go to the terminal and enter the `git merge` command as shown below![](../resources/rstudio_gitmerge1.png)3. if you now review the "History" tab of the "Review Changes" window, you will see the confluence of the two branches reflected in the commit graphic![](../resources/rstudio_gitmerge2.png):::```{cat}#| label: Fig7g#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig7g.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);% new commit on main\commit{right = 1.5cm of commitshash3}{commitshash7}{color_inactive}{commitshash7}{commitscomment7}\draw [<-,line width=3pt,draw=black!60] (commitshash3.east) -- (commitshash7.west);\commit{above = 3cm of commitshash7}{commitshash5}{color_inactive}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (commitshash3.north east) -- (commitshash5.south west);\commit{right = 1.5cm of commitshash5}{commitshash9}{color_inactive}{commitshash9}{commitscomment9}\draw [<-,line width=3pt,draw=black!60] (commitshash5.east) -- (commitshash9.west);\commit{right = 5cm of commitshash7}{commitshash11}{color_commit}{commitshash11}{commitscomment11}\draw [<-,line width=3pt,draw=black!60] (commitshash9.south east) -- (commitshash11.north west);\draw [<-,line width=3pt,draw=black!60] (commitshash7.east) -- (commitshash11.west);\master{below = 1cm of commitshash11} \branch{below = 1cm of commitshash9}{Feature} \HEAD{below = 0.1cm of master} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='markdown'}library(knitr)commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig7g.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig7g.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig7g.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig7g.pdf ../resources/Fig7g.png")```![](../resources/Fig7g.png)::: {.callout-note collapse="true"}## More information following the creation of the branch```{r, replace=TRUE}#| label: git-branch7g#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all``````{r, replace=TRUE}#| label: git-branch7g2#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset asciigit reflog```:::::: {.callout-note collapse="true"}## Another visual representation of the repository```{bash}#| label: gitDraw7g#| engine: bash#| echo: false#| eval: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit7g.png ```![](10_git_files/figure-html/drawGit7g.png){width=100%}:::::: {.callout-warning collapse="false"}If, when issuing a `git merge` command, you get a conflict message,please refer to the section on resolving conflicts below.:::## Delete a branchOnce the purpose of the branch has been fulfilled (for example todevelop a new feature) and the branch has been merged back into themain branch, you might consider deleting the branch so as to simplifythe commit history.Importantly, this action should only ever be performed after thebranch has been successfully merged into the main branch (in fact `gitwill not allow you to delete an un-merged branch unless you reallyfight for it).**Note also, this can only be performed on a local repository.**This procedure is only available via the terminal.<!-- The following chunk is used to get the hash of the Feature branch head so that later on in the doc, I can restore the branch. Since the next chunk will delete it. -->```{r}#| label: bash8h#| echo: false#| eval: true#| cache: falsehash =scan('~/tmp/Repo1/.git/refs/heads/Feature', what='character')hash_commit =strtrim(hash, width=5)knit_engines$set(bash1=function(options) { engine=options$engine options$code =gsub('#',hash_commit,options$code) code =paste("bash -c",shQuote(paste(options$code, collapse="\n"))) code =paste(options$engine_opts,code) out =system(code,intern=TRUE)engine_output(options,options$code,out) })``````{bash}#| label: git_branch8g#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git branch -d Feature``````{cat}#| label: Fig7h#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig7h.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);% new commit on main\commit{right = 1.5cm of commitshash3}{commitshash7}{color_inactive}{commitshash7}{commitscomment7}\draw [<-,line width=3pt,draw=black!60] (commitshash3.east) -- (commitshash7.west);\commit{above = 3cm of commitshash7}{commitshash5}{color_inactive}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (commitshash3.north east) -- (commitshash5.south west);\commit{right = 1.5cm of commitshash5}{commitshash9}{color_inactive}{commitshash9}{commitscomment9}\draw [<-,line width=3pt,draw=black!60] (commitshash5.east) -- (commitshash9.west);\commit{right = 5cm of commitshash7}{commitshash11}{color_commit}{commitshash11}{commitscomment11}\draw [<-,line width=3pt,draw=black!60] (commitshash9.south east) -- (commitshash11.north west);\draw [<-,line width=3pt,draw=black!60] (commitshash7.east) -- (commitshash11.west);\master{below = 1cm of commitshash11} \HEAD{below = 0.1cm of master} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig7h.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig7h.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig7h.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig7h.pdf ../resources/Fig7h.png")```![](../resources/Fig7h.png)::: {.callout-note collapse="true"}## More information following the creation of the branch```{r, replace=TRUE}#| label: git-branch7h#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all``````{r, replace=TRUE}#| label: git-branch7h2#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset asciigit reflog```:::::: {.callout-note collapse="true"}## Another visual representation of the repository```{bash}#| label: gitDraw7h#| engine: bash#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit7h.png ```![](10_git_files/figure-html/drawGit7h.png){width=100%}:::In order to facilitate the rest of the tutorial, I am going to**reset** the repository to a commit that precedes the merge. Theprocess of resetting will be covered later on in this tutorial.<!-- The hash variable needed for the code below comes from above bash8h -->```{bash, replace=TRUE}#| label: git_branch8h#| eval: false#| echo: false#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git branch -D Featuregit branch Feature #``````{bash, replace=TRUE}#| label: git_branch8h1#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reset --hard HEAD~1git clean -qfdxgit reflog expire --expire-unreachable=now --allgit gc --prune=now``````{cat}#| label: Fig7i#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig7i.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);% new commit on main\commit{right = 1.5cm of commitshash3}{commitshash7}{color_inactive}{commitshash7}{commitscomment7}\draw [<-,line width=3pt,draw=black!60] (commitshash3.east) -- (commitshash7.west);\commit{above = 3cm of commitshash7}{commitshash5}{color_inactive}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (commitshash3.north east) -- (commitshash5.south west);\commit{right = 1.5cm of commitshash5}{commitshash9}{color_inactive}{commitshash9}{commitscomment9}\draw [<-,line width=3pt,draw=black!60] (commitshash5.east) -- (commitshash9.west);\master{below = 1cm of commitshash7} \HEAD{below = 0.1cm of master} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig7i.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig7i.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig7i.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig7i.pdf ../resources/Fig7i.png")```![](../resources/Fig7i.png)::: {.callout-note collapse="true"}## More information following the creation of the branch```{r, replace=TRUE}#| label: git-branch9h3#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset asciigit log --graph --decorate --oneline --allgit reflog```:::## RebasingBranches are great for working on new features without disturbing astable codebase. However, the main branch may have changed orprogressed since the branch started. As a result, it may be buildingupon old code that may either no longer work or no longer beappropriate.Whilst you could attempt to manually apply the newer main code changesinto your branch, this is likely to be tedious and error prone.**Rebasing** is the process of changing the root (base) of the branchfrom one commit to another. In this way, the base of the branch can bemoved to the current HEAD of the main branch, thereby absorbing allthe updates from the main branch into the feature branch.This section builds on the repository created up to this point in thetutorial. To remind you, the repository currently looks like:::: {.callout-note collapse="true"}## Commands to create the repository```{bash}#| label: git_status8#| classes: bash#| engine: bash#| echo: true#| eval: truerm-rf ~/tmp/Repo1mkdir ~/tmp/Repo1cd ~/tmp/Repo1git init echo'File 1'> file1git add file1git commit -m'Initial repo and added file1'echo'---------------'>> file1mkdir dir1echo'* Notes'> dir1/file2git add file1 dir1/file2git commit -m'Modified file1 and added file2 (in dir1)'echo'---'>> dir1/file2echo'temp'> dir1/f.tmpecho'*.tmp'> .gitignoregit add .git commit -m'Modified file2, added .gitignore'git tag V.1git branch Featuregit checkout Featureecho'b'>> file1echo'File 3'> dir1/file3git add .git commit -m'New feature'git checkout mainecho' another bug fix'>> dir1/file2git add .git commit -m'Bug fix in file1'git checkout Featureecho' a modification'>> dir1/file3git add .git commit -m'Feature complete'```:::![](../resources/Fig7i.png)::: {.callout-note collapse="true"}## More information about this repository```{r, replace=TRUE}#| label: git-rebase1a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset ascii``````{r, replace=TRUE}#| label: git-rebase1b#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all``````{r, replace=TRUE}#| label: git-rebase1c#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reflog```:::::: {.panel-tabset}## Terminal```{bash}#| label: git-rebase3a#| echo: !expr -1#| eval: true#| error: true#| cache: false#| classes: bashcd ~/tmp/Repo1git checkout Feature``````{bash}#| label: git-rebase3b#| echo: !expr -1#| eval: true#| error: true#| cache: false#| classes: bashcd ~/tmp/Repo1git rebase main```## RstudioRebasing is not directly supported by Rstudio. 1. checkout the `Feature` branch using the "Switch branch" selector2. in the terminal, type `git rebase main` to rebase the `Feature` branch on the end of the `main` branch.3. if you now review the "History" tab of the "Review Changes" window, you will now see that the history is linear and the `Feature` branch stems from the end of the `main` branch. That is, we have moved the base of the `Feature` branch.![](../resources/rstudio_gitrebase1.png):::::: {.callout-note collapse="true"}## More information about this repository```{r, replace=TRUE}#| label: git-rebase4a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset ascii``````{r, replace=TRUE}#| label: git-rebase4b#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all``````{r, replace=TRUE}#| label: git-rebase4c#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reflog```:::```{cat}#| label: Fig8a#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig8a.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);%Bug fix\commit{right = 1.5cm of commitshash3}{commitshash4}{color_inactive}{commitshash4}{commitscomment4}\draw [<-,line width=3pt,draw=black!60] (commitshash3.east) -- (commitshash4.west);%New feature\commit{right = 1.5cm of commitshash4}{commitshash5}{color_inactive}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (commitshash4.east) -- (commitshash5.west);%Feature complete\commit{right = 1.5cm of commitshash5}{commitshash6}{color_commit}{commitshash6}{commitscomment6}\draw [<-,line width=3pt,draw=black!60] (commitshash5.east) -- (commitshash6.west);\master{below = 1cm of commitshash4} \branch{below = 1cm of commitshash6}{Feature} \HEAD{below = 0.1cm of Feature} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='markdown'}library(knitr)## commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)## commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')## commits = commits[rev(1:nrow(commits)),]## colnames(commits) <- c('hash','comment')## commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')## lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)## lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')## lookup = lookup %>% mutate(Ref = ifelse(grepl("main", V2), "main", NA))commits = system("cd ~/tmp/Repo1; git log --oneline | awk '{ print $1,\"\\t\",substr($0, 9) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),10),'..') #print(commits)# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig8a.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig8a.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig8a.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig8a.pdf ../resources/Fig8a.png")```![](../resources/Fig8a.png)If the rebased commits were previously on a remote repository(hopefully you checked to make sure noone was relying on any of thecommits that have been squashed), then it will be necessary to force apush on this repository.# Undoing (rolling back) changesOne of the real strengths of a versioning system is the ability toroll back to a previous state when changes have been found tointroduce undesirable or unintended consequences. There are alsomultiple different stages from which to roll back. For example, do wewant to revert from committed states or just unstage a file or files.To illustrate the various ways to roll back within a repository, wewill start with a small repository comprising of a single branch andjust three commits. This repository will mimic a repository createdearlier in this tutorial (before we started branching).::: {.callout-note collapse="true"}## Commands to create the repository```{bash}#| label: git_status9#| classes: bash#| engine: bash#| echo: true#| cache: false#| eval: truerm-rf ~/tmp/Repo1mkdir ~/tmp/Repo1cd ~/tmp/Repo1git init echo'File 1'> file1git add file1git commit -m'Initial repo and added file1'echo'---------------'>> file1mkdir dir1echo'* Notes'> dir1/file2git add file1 dir1/file2git commit -m'Modified file1 and added file2 (in dir1)'echo'---'>> dir1/file2echo'temp'> dir1/f.tmpecho'*.tmp'> .gitignoregit add .git commit -m'Modified file2, added .gitignore'git tag V.1``````{bash}#| label: git-commit1a1000#| echo: !expr -1#| eval: false#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1cat .git/refs/heads/main```:::::: {.callout-note collapse="true"}## More information about this repository```{r, replace=TRUE}#| label: git-rollback1a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset ascii``````{r, replace=TRUE}#| label: git-rollback1b#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all``````{r, replace=TRUE}#| label: git-rollback1c#| echo: false#| eval: false#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reflog```:::```{cat}#| label: Fig-advanced7#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig-advanced7.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\usetikzlibrary{arrows.meta}\definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_tree}{HTML}{9ACD32} %\definecolor{color_tree}{rgb}{0.78,0.86,0.27}\definecolor{color_commit}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\definecolor{color_master}{rgb}{0.36,0.27,0.87}\definecolor{color_head}{HTML}{6495ED} %\definecolor{color_head}{HTML}{6495ED}\definecolor{color_index}{HTML}{E0FFFF} %\definecolor{color_index}{rgb}{1,1,1}\definecolor{color_file}{rgb}{1,1,1}\tikzstyle{TARGET} = [font={\fontspec[Scale=2]{NotoSans-Regular}}] \tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}] \tikzstyle{TREE} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}] \tikzstyle{fileText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] %Define a file \newcommand{\file}[4] {\def\corner{0.15in};\def\cornerradius{0.02in};\def\lwidth{0.02in};\def\h{0.5in};\def\w{0.85in};\def\nline{0};\def\iconmargin{0.1in};\def\topmargin{0.3in};\node at (#1) {\begin{tikzpicture}\coordinate (nw) at ($(-0.05in*1,-0.15in*1)$);\coordinate (#3) at (#1); \coordinate (ne0) at ($(nw) + (\w, 0)$);\coordinate (ne1) at ($(ne0) - (\corner, 0)$);\coordinate (ne2) at ($(ne0) - (0, \corner)$);\coordinate (se) at ($(ne0) + (0, -\h)$); \filldraw [-, line width = \lwidth, fill=#4] (nw) -- (ne1) -- (ne2)[rounded corners=\cornerradius]--(se) -- (nw|-se) -- cycle;\draw [-, line width = \lwidth] (ne1) [rounded corners=\cornerradius]-- (ne1|-ne2) -- (ne2);\node [anchor=north west,TREE] at (nw) {#2};\foreach \k in {0,...,\nline}{\draw [-, line width = \lwidth, line cap=round] ($(nw|-se) + (\iconmargin,\iconmargin) + (0,{(\k-1)/(\nline-1)*(\h - \iconmargin - \topmargin)})$)-- ++ ($(\w,0) - 2*(\iconmargin,0)$);}\end{tikzpicture}};}% end of file definition\begin{tikzpicture}\coordinate (G_ul) at (0,0);\coordinate (G_ll) at ($(G_ul) +(0,-10)$);\coordinate (G_lr) at ($(G_ll) +(4,0)$);\coordinate (G_ur) at ($(G_ul) +(4,0)$);\node[TREE,anchor=west] at ($(G_ul) +(0,-0.5)$) (git) {.git/};\file{$(git.west) +(1.5,-2)$}{HEAD}{HEAD}{color_head}\file{$(HEAD) +(0,-2)$}{index}{index}{color_index}\draw[] (G_ul) -- (G_ll) -- (G_lr) -- (G_ur) -- cycle;%refs\node[TREE,anchor=west] at ($(G_ul) +(-5,-0.5)$) (git_ref) {.git/refs/};\file{$(git_ref.west) +(1.5,-2)$}{\textcolor{white}{main}}{master}{color_master}\draw[] ($(G_ul) +(-5,0)$) -- ++(0,-10) -- ++(4.9,0) -- ++(0,10) -- cycle;%files\node[TREE,anchor=west] at ($(G_ul) +(-21,-0.5)$) (root) {/};\file{$(root.west) +(1.7,-2)$}{\textcolor{black}{file1}}{file1}{color_file}\node[TREE,anchor=west] at ($(root |- file1) +(0.4,-2.2)$) (dir1) {dir1/};\draw [very thick] (root) -- (root |- file1) -- ++(0.2,0);\draw [very thick] (root) -- (root |- dir1) -- ++(0.2,0);\file{$(dir1.west) +(1.7,-1.0)$}{\textcolor{black}{file2}}{file2}{color_file}\draw [very thick] ($(dir1.west) +(0.2,-0.2)$) -- ($(dir1.west |- file2) +(0.2,0)$) -- ++(0.2,0);\file{$(file2.west) +(0,-1.5)$}{\textcolor{black!70}{f.tmp}}{file3}{color_file}\draw [very thick] ($(dir1.west) +(0.2,-0.2)$) -- ($(dir1.west |- file3) +(0.2,0)$) -- ++(0.2,0);\file{$(root.west) +(1.7,-8.5)$}{\textcolor{black}{.giti.}}{file4}{color_file}\draw [very thick] (root) -- (root |- file4) -- ++(0.2,0);\begin{pgfonlayer}{background}\draw[fill=color_workspace!20] ($(G_ul) +(-21,0)$) -- ++(0,-10) -- ++(3.9,0) -- ++(0,10) -- cycle;\end{pgfonlayer}%objects\node[TREE,anchor=west] at ($(G_ul) +(-17,-0.5)$) (git_object) {.git/objects/};\file{$(git_object.west) +(1.5,-2)$}{\textcolor{black}{hashblob1}}{blob1}{color_workspace!20}\file{$(git_object.west) +(1.5,-3.5)$}{\textcolor{black}{hashblob3}}{blob2}{color_workspace}\file{$(git_object.west |- file2.north) +(1.5,-0)$}{\textcolor{black}{hashblob2}}{blob3}{color_workspace!20}\file{$(git_object.west |- file3.north) +(1.5,0)$}{\textcolor{black}{hashblob5}}{blob4}{color_workspace}\file{$(git_object.west |- file4.north) +(1.5,0)$}{\textcolor{black}{hashblob4}}{blob5}{color_workspace}\draw[] ($(G_ul) +(-17,0)$) -- ++(0,-10) -- ++(11.9,0) -- ++(0,10) -- cycle;%trees\file{$(blob1.west) +(5,0)$}{\textcolor{black}{hashtree1}}{tree1}{color_tree!20}\file{$($(blob3.west) !0.5! (blob3.west)$) +(3,0)$}{\textcolor{black}{hashtree3}}{tree2}{color_tree!20}\file{$($(blob2.west) !0.5! (blob3.west)$) +(5.75,0)$}{\textcolor{black}{hashtree2}}{tree3}{color_tree!20}\file{$($(blob4.west) !0.5! (blob4.west)$) +(3,0)$}{\textcolor{black}{hashtree5}}{tree4}{color_tree}\file{$($(blob4.west) !0.5! (blob5.west)$) +(5.75,0)$}{\textcolor{black}{hashtree4}}{tree5}{color_tree}%commits\file{$(tree1.west) +(3,0)$}{\textcolor{black}{hashcommits1}}{commit1}{color_commit!20}\file{$(tree3.west) +(2.75,0)$}{\textcolor{black}{hashcommits2}}{commit2}{color_commit!20}\file{$(tree5.west) +(2.75,0)$}{\textcolor{black}{hashcommits3}}{commit3}{color_commit}%arrows\draw[->,very thick,draw=black] ($(HEAD) +(-1,0)$) -- ($(master) +(1.2,0)$);\draw[->,very thick] ($(master) +(-1,0)$) -- ($(commit3) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(commit1) +(-1,0)$) -- ($(tree1) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(tree1) +(-1,0)$) -- ($(blob1) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(commit2) +(-1,0)$) -- ($(tree3) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(tree3) +(-1,0)$) -- ($(tree2) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(tree3) +(-1,0)$) -- ($(blob2) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(tree2) +(-1,0)$) -- ($(blob3) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(commit2) +(0,0.6)$) -- ($(commit1) +(0,-0.7)$);\draw[->,very thick] ($(commit3) +(-1,0)$) -- ($(tree5) +(1.2,0)$);\draw[->,very thick] ($(tree5) +(-1,0)$) -- ($(tree4) +(1.2,0)$);\draw[->,very thick] ($(tree5) +(-1,0)$) -- ($(blob5) +(1.2,0)$);\draw[->,very thick] ($(tree4) +(-1,0)$) -- ($(blob4) +(1.2,0)$);\draw[->,very thick] ($(tree5) +(-1,0)$) -- ($(blob2) +(1.2,0)$);\draw[->,very thick] ($(commit3) +(0,0.6)$) -- ($(commit2) +(0,-0.7)$);\draw[<-,very thick,dashed] ($(blob2) +(-1.2,0)$) -- ($(file1) +(1.2,0)$);\draw[<-,very thick,dashed] ($(blob4) +(-1.2,0)$) -- ($(file2) +(1.2,0)$);\draw[<-,very thick,dashed] ($(blob5) +(-1.2,0)$) -- ($(file4) +(1.2,0)$);\draw[->,very thick] ($(index) +(-1,0)$) -- ($(blob3) +(1.2,0)$);\draw[->,very thick] ($(index) +(-1,0)$) -- ($(blob2) +(1.2,0)$);\end{tikzpicture}\end{preview}\end{document}``````{r scal7a, cache=FALSE, echo=FALSE, results='markup'}library(knitr)# commithash = scan("~/tmp/Repo1/.git/refs/heads/main", what = "character")has_commits <- strtrim(hash, width = 5)#hash_commits = c(hash_commits, strtrim(hash, width = 5))# treehash = system(paste0("cd ~/tmp/Repo1; git cat-file -p ", hash_commits[2]), intern = TRUE)hash = read.table(textConnection(hash[1]))hash_tree = c(hash_tree, strtrim(hash$V2, 5))hash = system(paste0('cd ~/tmp/Repo1; git cat-file -p HEAD^{tree}'), intern=TRUE)hash = read.table(textConnection(hash))hash_tree = c(hash_tree, strtrim(hash$V3[hash$V2 == "tree"], 5))#blobhash = system('cd ~/tmp/Repo1; git ls-files --stage', intern=TRUE)hash = read.table(textConnection(hash))hash_blob = unique(c(hash_blob,strtrim(hash$V2,5)))system(paste0("sed -i 's/hashblob1/", hash_blob[1], "/g' ../resources/Fig-advanced7.tikz"))system(paste0("sed -i 's/hashblob2/", hash_blob[2], "/g' ../resources/Fig-advanced7.tikz"))system(paste0("sed -i 's/hashblob3/", hash_blob[3], "/g' ../resources/Fig-advanced7.tikz"))system(paste0("sed -i 's/hashblob4/", hash_blob[4], "/g' ../resources/Fig-advanced7.tikz"))system(paste0("sed -i 's/hashblob5/", hash_blob[5], "/g' ../resources/Fig-advanced7.tikz"))system(paste0("sed -i 's/hashtree1/", hash_tree[1], "/g' ../resources/Fig-advanced7.tikz"))system(paste0("sed -i 's/hashtree2/", hash_tree[2], "/g' ../resources/Fig-advanced7.tikz"))system(paste0("sed -i 's/hashtree3/", hash_tree[3], "/g' ../resources/Fig-advanced7.tikz"))system(paste0("sed -i 's/hashtree4/", hash_tree[4], "/g' ../resources/Fig-advanced7.tikz"))system(paste0("sed -i 's/hashtree5/", hash_tree[5], "/g' ../resources/Fig-advanced7.tikz"))system(paste0("sed -i 's/hashcommits1/", hash_commits[1], "/g' ../resources/Fig-advanced7.tikz"))system(paste0("sed -i 's/hashcommits2/", hash_commits[2], "/g' ../resources/Fig-advanced7.tikz"))system(paste0("sed -i 's/hashcommits3/", hash_commits[3], "/g' ../resources/Fig-advanced7.tikz"))system("xelatex -output-directory=../resources ../resources/Fig-advanced7.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig-advanced7.pdf ../resources/Fig-advanced7.png")```![](../resources/Fig-advanced7.png)The above diagram shows that both `HEAD` and `main` point at thesame stage (all three files). **Again, remember that the SHA-1 hasvalues will be different in your repo so in the following, you willneed to use the SHA value that corresponds to the item in your list**.With additional commits and activity, the above schematic will rapidlybecome very busy and complex. As a result, we will now switch to asimpler schematic that focuses only on the commits and referencesthereof (`HEAD`, `main` and _branches_).```{cat}#| label: Fig9a#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig9a.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);\master{below = 1cm of commitshash3} \HEAD{below = 0.1cm of master} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)## commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)## commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')## commits = commits[rev(1:nrow(commits)),]## colnames(commits) <- c('hash','comment')## commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')## lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)## lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')## lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))# get the commits from .git/logs/HEADcommits = system("cd ~/tmp/Repo1; git log --oneline | awk '{ print $1,\"\\t\",substr($0, 9) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),10),'..') for (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig9a.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig9a.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig9a.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig9a.pdf ../resources/Fig9a.png")```![](../resources/Fig9a.png)Recall that a git repository comprises multiple levels in whichchanges are recorded:- there is the **Workspace** (which is essentially the actual files and folders that you directly edit).- there is the **Staging area** (or index which is a record of which files are next to be committed).- there is the **Local repository** (the actual commits).- and finally, three is the **remote repository** (a remote store of commits).As such, there are multiple levels from which changes could be undone.Furthermore, we might want to undo changes at the commit or individualfile level. For example, we might decide that we have made a localcommit that introduced an issue and we now wish to return back to thestate prior to this commit. Alternatively, we might have justaccidentally staged a file (yet not committed it) and now we want tounstage it.```{tikz}%| label: Figs9a%| engine: tikz%| echo: false%| cache: true%| include: false%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\tikzstyle{refText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] \begin{tikzpicture}%[every node/.style={inner sep=0,outer sep=0, minimum height = 0}]\commit{}{A}{color_inactive}{}{}\commit{right = 1cm of A}{B}{color_commit}{}{}\commit{right = 1cm of B}{C}{color_detached}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\draw [-,line width=3pt,draw=black!60] (B) -- (A);\draw [-,line width=3pt,draw=black!60] (C) -- (B);\end{tikzpicture}``````{bash}#| label: Figs9a-conv#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Figs9a-1.pdf 10_git_files/figure-html/Figs9a-1.png ``````{tikz}%| label: Figs9b%| engine: tikz%| echo: false%| cache: true%| include: false%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\tikzstyle{refText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] \begin{tikzpicture}%[every node/.style={inner sep=0,outer sep=0, minimum height = 0}]\commit{}{A}{color_inactive}{}{}\commit{right = 1cm of A}{B}{color_commit}{}{}\commit{right = 1cm of B}{C}{color_detached!30}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\draw [-,line width=3pt,draw=black!60] (B) -- (A);\draw [->,dashed,line width=3pt,draw=black!60] (C) -- (B);\end{tikzpicture}``````{bash}#| label: Figs9b-conv#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Figs9b-1.pdf 10_git_files/figure-html/Figs9b-1.png ``````{tikz}%| label: Figs9c%| engine: tikz%| echo: false%| cache: true%| include: false%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\tikzstyle{refText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] \begin{tikzpicture}%[every node/.style={inner sep=0,outer sep=0, minimum height = 0}]\commit{}{A}{color_commit}{}{}\commit{right = 1cm of A}{B}{color_detached!30}{}{}\commit{right = 1cm of B}{C}{color_detached!30}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\draw [->, dashed, line width=3pt,draw=black!60] (B) -- (A);\draw [->,dashed,line width=3pt,draw=black!60] (C) -- (B);\end{tikzpicture}``````{bash}#| label: Figs9c-conv#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Figs9c-1.pdf 10_git_files/figure-html/Figs9c-1.png ``````{tikz}%| label: Figs9d%| engine: tikz%| echo: false%| cache: true%| include: false%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\tikzstyle{refText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] \begin{tikzpicture}%[every node/.style={inner sep=0,outer sep=0, minimum height = 0}]\commit{}{A}{color_inactive}{}{}\commit{right = 1cm of A}{B}{color_commit}{}{}\commit{right = 1cm of B}{C}{color_detached}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\draw [-,line width=3pt,draw=black!60] (B) -- (A);\end{tikzpicture}``````{bash}#| label: Figs9d-conv#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Figs9d-1.pdf 10_git_files/figure-html/Figs9d-1.png ``````{tikz}%| label: Figs9e%| engine: tikz%| echo: false%| cache: true%| include: false%| dependson: common%| class: tikz%| engine-opts:%| template: "../resources/tikz-minimal.tex"\input{../resources/common.tikz}\tikzstyle{refText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] \begin{tikzpicture}%[every node/.style={inner sep=0,outer sep=0, minimum height = 0}]\commit{}{A}{color_inactive}{}{}\commit{right = 1cm of A}{B}{color_commit}{}{}\commit{right = 1cm of B}{C}{color_detached}{}{}\draw [-,line width=3pt,draw=black!60] (A) -- ++(-1,0);\draw [-,line width=3pt,draw=black!60] (B) -- (A);\draw [-,line width=3pt,draw=black!60] (C) -- (B);\draw [->,line width=3pt,draw=black!60] (A) to[out=45] (C);\end{tikzpicture}``````{bash}#| label: Figs9e-conv#| cache: false#| echo: falseconvert-trim +repage -density 300 -resize 20% 10_git_files/figure-html/Figs9e-1.pdf 10_git_files/figure-html/Figs9e-1.png ```<table class='table table-primary table-bordered table-sm paramsTable' id = 'rollback-table'><thead><tr class = 'header'><th>Action</th><th width='30%'>Command</th><th>Notes</th></tr></thead><tbody><tr><td colspan='3'><i>Commit level</i></td></tr><tr><td>Undo to a particular local commit<br>![](10_git_files/figure-html/Figs9a-1.png)</td><td>`git reset --soft <commit>`</td><td>HEAD is moved to the nominated <commit>. IT DOES NOT alter index or the workspace</td></tr><tr><td>Roll back to the the previous commit<br>![](10_git_files/figure-html/Figs9b-1.png)</td><td>`git reset --hard <commit>`</td><td>Resets the Index and Workspace</td></tr><tr><td>Roll back over the last two commits<br>![](10_git_files/figure-html/Figs9c-1.png)</td><td>`git reset --hard HEAD~2`</td><td>Roll back over the last two commits</td></tr><tr><td>Inspect an old commit<br>![](10_git_files/figure-html/Figs9d-1.png)</td><td>`git checkout <commit>`</td><td>moves the HEAD and modifies the workspace to reflect its state at <commit></td></tr><tr><td>Roll back the changes introduced by commit so that a new commit resembles a previous state<br>![](10_git_files/figure-html/Figs9e-1.png)</td><td>`git revert HEAD`</td><td>Creates a new commit that reverses the changes introduced by the last commit.Revert creates a new revision history that adds onto existing history and is therefore safe to use on a branch that has been pushed to a remote.</td></tr></tbody></table>Now lets say we wanted to roll back to the state before we added `.gitignore` and modified `dir1/file2`.That is, we want to roll-back to commit ``r commits$hash[2]``.We have three main choices:- **reset** - this allows us to remove all commits back to a nominated commit. _Resetting_ is a irreversible process as it totally removes commits from the history. A _reset_ should only ever be used if you are sure you want to permanently remove the changes introduced via one or more commits. **A reset should never be performed on a branch that exists in a remote repository**- **revert** - this allows us to skip the most recent commit. That is, a _revert_ rolls back to a previous commit and then apply that state to a new commit. Unlike a _reset_, all commits remain safely in the git history and can target a single commit.- **branch** - this allows us to safely take the project (or part of the project) in an experimental direction that might involve dramatic deviations in files without interrupting the main thread of the project. At some point, if the new direction proves useful, the changes can be merged back into the main branch. We will expore _branching_ in <a href="#Branching">the section on branching</a>.Normally we would not perform all three. Rather, we would select themost appropriate one depending on the context and goal. Nevertheless,this is a tutorial and therefore we will perform all three. In orderto ensure that we start from the same point for each demonstration,prior to each demonstration, we will aggressively reset the repositoryback to the state it was at commit ``r commits$hash[2]``.## ResetReset is not directly supported by Rstudio - use the terminal for this section.### Soft resetWhen we perform a **soft reset**, we move the head to the nominatedcommit, but the workspace is unchanged.```{r}#| label: bash9#| echo: false#| eval: true#| cache: falseknit_engines$set(bash1=function(options) { engine=options$engine options$code =gsub("#", commits$hash[2], options$code) code =paste("bash -c",shQuote(paste(options$code, collapse="\n"))) code =paste(options$engine_opts,code) out =system(code,intern=TRUE)engine_output(options,options$code,out) })``````{r, replace=TRUE}#| label: git-reset9b#| echo: !expr -1#| eval: false#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reset --soft #```::: {.callout-note collapse="true"}## More information about this repository```{r, replace=TRUE}#| label: git-rollback2d#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset ascii``````{r, replace=TRUE}#| label: git-rollback2e#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all``````{r, replace=TRUE}#| label: git-rollback2f#| echo: false#| eval: false#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reflog```:::```{cat}#| label: Fig-advanced8#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig-advanced8.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\usetikzlibrary{arrows.meta}\definecolor{color_workspace}{rgb}{0.12,0.6,0.51}\definecolor{color_tree}{HTML}{9ACD32} %\definecolor{color_tree}{rgb}{0.78,0.86,0.27}\definecolor{color_commit}{rgb}{0.9,0.9,0.2}\definecolor{color_remote}{rgb}{1,0.55,0.15}\definecolor{color_master}{rgb}{0.36,0.27,0.87}\definecolor{color_head}{HTML}{6495ED} %\definecolor{color_head}{HTML}{6495ED}\definecolor{color_index}{HTML}{E0FFFF} %\definecolor{color_index}{rgb}{1,1,1}\definecolor{color_file}{rgb}{1,1,1}\tikzstyle{TARGET} = [font={\fontspec[Scale=2]{NotoSans-Regular}}] \tikzstyle{CODE} = [font={\fontspec[Scale=2]{InconsolataSemiCondensed-Regular}}] \tikzstyle{TREE} = [font={\fontspec[Scale=1.5]{InconsolataSemiCondensed-Regular}}] \tikzstyle{fileText} = [font={\fontspec[Scale=1.1]{InconsolataSemiCondensed-Regular}}] %Define a file \newcommand{\file}[4] {\def\corner{0.15in};\def\cornerradius{0.02in};\def\lwidth{0.02in};\def\h{0.5in};\def\w{0.85in};\def\nline{0};\def\iconmargin{0.1in};\def\topmargin{0.3in};\node at (#1) {\begin{tikzpicture}\coordinate (nw) at ($(-0.05in*1,-0.15in*1)$);\coordinate (#3) at (#1); \coordinate (ne0) at ($(nw) + (\w, 0)$);\coordinate (ne1) at ($(ne0) - (\corner, 0)$);\coordinate (ne2) at ($(ne0) - (0, \corner)$);\coordinate (se) at ($(ne0) + (0, -\h)$); \filldraw [-, line width = \lwidth, fill=#4] (nw) -- (ne1) -- (ne2)[rounded corners=\cornerradius]--(se) -- (nw|-se) -- cycle;\draw [-, line width = \lwidth] (ne1) [rounded corners=\cornerradius]-- (ne1|-ne2) -- (ne2);\node [anchor=north west,TREE] at (nw) {#2};\foreach \k in {0,...,\nline}{\draw [-, line width = \lwidth, line cap=round] ($(nw|-se) + (\iconmargin,\iconmargin) + (0,{(\k-1)/(\nline-1)*(\h - \iconmargin - \topmargin)})$)-- ++ ($(\w,0) - 2*(\iconmargin,0)$);}\end{tikzpicture}};}% end of file definition\begin{tikzpicture}\coordinate (G_ul) at (0,0);\coordinate (G_ll) at ($(G_ul) +(0,-10)$);\coordinate (G_lr) at ($(G_ll) +(4,0)$);\coordinate (G_ur) at ($(G_ul) +(4,0)$);\node[TREE,anchor=west] at ($(G_ul) +(0,-0.5)$) (git) {.git/};\file{$(git.west) +(1.5,-2)$}{HEAD}{HEAD}{color_head}\file{$(HEAD) +(0,-2)$}{index}{index}{color_index}\draw[] (G_ul) -- (G_ll) -- (G_lr) -- (G_ur) -- cycle;%refs\node[TREE,anchor=west] at ($(G_ul) +(-5,-0.5)$) (git_ref) {.git/refs/};\file{$(git_ref.west) +(1.5,-2)$}{\textcolor{white}{main}}{master}{color_master}\draw[] ($(G_ul) +(-5,0)$) -- ++(0,-10) -- ++(4.9,0) -- ++(0,10) -- cycle;%files\node[TREE,anchor=west] at ($(G_ul) +(-21,-0.5)$) (root) {/};\file{$(root.west) +(1.7,-2)$}{\textcolor{black}{file1}}{file1}{color_file}\node[TREE,anchor=west] at ($(root |- file1) +(0.4,-2.2)$) (dir1) {dir1/};\draw [very thick] (root) -- (root |- file1) -- ++(0.2,0);\draw [very thick] (root) -- (root |- dir1) -- ++(0.2,0);\file{$(dir1.west) +(1.7,-1.0)$}{\textcolor{black}{file2}}{file2}{color_file}\draw [very thick] ($(dir1.west) +(0.2,-0.2)$) -- ($(dir1.west |- file2) +(0.2,0)$) -- ++(0.2,0);\file{$(file2.west) +(0,-1.5)$}{\textcolor{black!70}{f.tmp}}{file3}{color_file}\draw [very thick] ($(dir1.west) +(0.2,-0.2)$) -- ($(dir1.west |- file3) +(0.2,0)$) -- ++(0.2,0);\file{$(root.west) +(1.7,-8.5)$}{\textcolor{black}{.giti.}}{file4}{color_file}\draw [very thick] (root) -- (root |- file4) -- ++(0.2,0);\begin{pgfonlayer}{background}\draw[fill=color_workspace!20] ($(G_ul) +(-21,0)$) -- ++(0,-10) -- ++(3.9,0) -- ++(0,10) -- cycle;\end{pgfonlayer}%objects\node[TREE,anchor=west] at ($(G_ul) +(-17,-0.5)$) (git_object) {.git/objects/};\file{$(git_object.west) +(1.5,-2)$}{\textcolor{black}{hashblob1}}{blob1}{color_workspace!20}\file{$(git_object.west) +(1.5,-3.5)$}{\textcolor{black}{hashblob3}}{blob2}{color_workspace}\file{$(git_object.west |- file2.north) +(1.5,-0)$}{\textcolor{black}{hashblob2}}{blob3}{color_workspace!20}\file{$(git_object.west |- file3.north) +(1.5,0)$}{\textcolor{black}{hashblob5}}{blob4}{color_workspace}\file{$(git_object.west |- file4.north) +(1.5,0)$}{\textcolor{black}{hashblob4}}{blob5}{color_workspace}\draw[] ($(G_ul) +(-17,0)$) -- ++(0,-10) -- ++(11.9,0) -- ++(0,10) -- cycle;%trees\file{$(blob1.west) +(5,0)$}{\textcolor{black}{hashtree1}}{tree1}{color_tree!20}\file{$($(blob3.west) !0.5! (blob3.west)$) +(3,0)$}{\textcolor{black}{hashtree3}}{tree2}{color_tree!20}\file{$($(blob2.west) !0.5! (blob3.west)$) +(5.75,0)$}{\textcolor{black}{hashtree2}}{tree3}{color_tree!20}\file{$($(blob4.west) !0.5! (blob4.west)$) +(3,0)$}{\textcolor{black}{hashtree5}}{tree4}{color_tree}\file{$($(blob4.west) !0.5! (blob5.west)$) +(5.75,0)$}{\textcolor{black}{hashtree4}}{tree5}{color_tree}%commits\file{$(tree1.west) +(3,0)$}{\textcolor{black}{hashcommits1}}{commit1}{color_commit!20}\file{$(tree3.west) +(2.75,0)$}{\textcolor{black}{hashcommits2}}{commit2}{color_commit!20}\file{$(tree5.west) +(2.75,0)$}{\textcolor{black}{hashcommits3}}{commit3}{color_commit}%arrows\draw[->,very thick,draw=black] ($(HEAD) +(-1,0)$) -- ($(master) +(1.2,0)$);\draw[->,very thick] ($(master) +(-1,0)$) -- ($(commit3) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(commit1) +(-1,0)$) -- ($(tree1) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(tree1) +(-1,0)$) -- ($(blob1) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(commit2) +(-1,0)$) -- ($(tree3) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(tree3) +(-1,0)$) -- ($(tree2) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(tree3) +(-1,0)$) -- ($(blob2) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(tree2) +(-1,0)$) -- ($(blob3) +(1.2,0)$);\draw[->,very thick,draw=gray] ($(commit2) +(0,0.6)$) -- ($(commit1) +(0,-0.7)$);\draw[->,very thick] ($(commit3) +(-1,0)$) -- ($(tree5) +(1.2,0)$);\draw[->,very thick] ($(tree5) +(-1,0)$) -- ($(tree4) +(1.2,0)$);\draw[->,very thick] ($(tree5) +(-1,0)$) -- ($(blob5) +(1.2,0)$);\draw[->,very thick] ($(tree4) +(-1,0)$) -- ($(blob4) +(1.2,0)$);\draw[->,very thick] ($(tree5) +(-1,0)$) -- ($(blob2) +(1.2,0)$);\draw[->,very thick] ($(commit3) +(0,0.6)$) -- ($(commit2) +(0,-0.7)$);\draw[<-,very thick,dashed] ($(blob2) +(-1.2,0)$) -- ($(file1) +(1.2,0)$);\draw[<-,very thick,dashed] ($(blob4) +(-1.2,0)$) -- ($(file2) +(1.2,0)$);\draw[<-,very thick,dashed] ($(blob5) +(-1.2,0)$) -- ($(file4) +(1.2,0)$);\draw[->,very thick] ($(index) +(-1,0)$) -- ($(blob3) +(1.2,0)$);\draw[->,very thick] ($(index) +(-1,0)$) -- ($(blob2) +(1.2,0)$);\draw[->,very thick] ($(index) +(-1,0)$) -- ($(blob5) +(1.2,0)$);\end{tikzpicture}\end{preview}\end{document}``````{r scal8a, cache=FALSE, echo=FALSE, results='markup'}library(knitr)# commithash = scan("~/tmp/Repo1/.git/refs/heads/main", what = "character")has_commits <- strtrim(hash, width = 5)#hash_commits = c(hash_commits, strtrim(hash, width = 5))# treehash = system(paste0("cd ~/tmp/Repo1; git cat-file -p ", hash_commits[2]), intern = TRUE)hash = read.table(textConnection(hash[1]))hash_tree = c(hash_tree, strtrim(hash$V2, 5))hash = system(paste0('cd ~/tmp/Repo1; git cat-file -p HEAD^{tree}'), intern=TRUE)hash = read.table(textConnection(hash))hash_tree = c(hash_tree, strtrim(hash$V3[hash$V2 == "tree"], 5))#blobhash = system('cd ~/tmp/Repo1; git ls-files --stage', intern=TRUE)hash = read.table(textConnection(hash))hash_blob = unique(c(hash_blob,strtrim(hash$V2,5)))system(paste0("sed -i 's/hashblob1/", hash_blob[1], "/g' ../resources/Fig-advanced8.tikz"))system(paste0("sed -i 's/hashblob2/", hash_blob[2], "/g' ../resources/Fig-advanced8.tikz"))system(paste0("sed -i 's/hashblob3/", hash_blob[3], "/g' ../resources/Fig-advanced8.tikz"))system(paste0("sed -i 's/hashblob4/", hash_blob[4], "/g' ../resources/Fig-advanced8.tikz"))system(paste0("sed -i 's/hashblob5/", hash_blob[5], "/g' ../resources/Fig-advanced8.tikz"))system(paste0("sed -i 's/hashtree1/", hash_tree[1], "/g' ../resources/Fig-advanced8.tikz"))system(paste0("sed -i 's/hashtree2/", hash_tree[2], "/g' ../resources/Fig-advanced8.tikz"))system(paste0("sed -i 's/hashtree3/", hash_tree[3], "/g' ../resources/Fig-advanced8.tikz"))system(paste0("sed -i 's/hashtree4/", hash_tree[4], "/g' ../resources/Fig-advanced8.tikz"))system(paste0("sed -i 's/hashtree5/", hash_tree[5], "/g' ../resources/Fig-advanced8.tikz"))system(paste0("sed -i 's/hashcommits1/", hash_commits[1], "/g' ../resources/Fig-advanced8.tikz"))system(paste0("sed -i 's/hashcommits2/", hash_commits[2], "/g' ../resources/Fig-advanced8.tikz"))system(paste0("sed -i 's/hashcommits3/", hash_commits[3], "/g' ../resources/Fig-advanced8.tikz"))system("xelatex -output-directory=../resources ../resources/Fig-advanced8.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig-advanced8.pdf ../resources/Fig-advanced8.png")```![](../resources/Fig-advanced8.png)::: {.callout-note collapse="true"}## Another visual representation of the repository```{bash}#| label: gitDraw8a#| engine: bash#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit8a.png ```![](10_git_files/figure-html/drawGit8a.png){width=100%}:::::: {.callout-note collapse="true"}## More information about this repository```{r, replace=TRUE}#| label: git-reset2a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset ascii``````{r, replace=TRUE}#| label: git-reset2b#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all``````{r, replace=TRUE}#| label: git-reset2c#| echo: false#| eval: false#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reflog```:::### Hard resetWhen we perform a **hard reset**, we not only move the head to thenominated commit, but the workspace is altered to reflect theworkspace that existed when that commit was originally performed.As I am about to demonstrate this on a repo that I have just performeda soft reset on, I am first going to start by re-establishing theoriginal repository. If you have not just run a soft reset, thenignore the following.::: {.callout-note collapse="true"}## Re-establish repository```{r, replace=TRUE}#| label: git-reset3a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reset --hard V.1git clean -qfdxgit reflog expire --expire-unreachable=now --allgit gc --prune=now```:::Now we are in a position to perform the hard reset.```{r, cache=FALSE, echo=FALSE, results='markdown'}library(knitr)commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub(".*:", "", commits$comment), 15), "..")``````{r, replace=TRUE}#| label: git-reset9c#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reset --hard #```::: {.callout-note collapse="true"}## More information about this repository```{r, replace=TRUE}#| label: git-rollback3d#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset ascii```Notice that `.gitignore` is not not present.```{r, replace=TRUE}#| label: git-rollback3e#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all``````{r, replace=TRUE}#| label: git-rollback3f#| echo: !expr -1#| eval: false#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reflog```Note, however, if we looked at the log, it would be as if the previouscommit had not occurred. For this reason, care must be exercised whenusing reset on remote repositories since others may be relying on aspecific point in the repo history that you may have just erased.:::```{cat}#| label: Fig9b#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig9b.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_commit}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commitz{right = 1.5cm of commitshash2}{commitshash3}{color_zombie}{commitshash3}{commitscomment3}{black!20!white}\draw [<-,line width=3pt,draw=black!20] (commitshash2.east) -- (commitshash3.west);\master{below = 1cm of commitshash2} \HEAD{below = 0.1cm of master} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)## get these from a chunk before had reset so that it has all commits## commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)## commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')## commits = commits[rev(1:nrow(commits)),]## colnames(commits) <- c('hash','comment')## commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig9b.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig9b.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig9b.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig9b.pdf ../resources/Fig9b.png")```![](../resources/Fig9b.png)::: {.callout-note collapse="true"}## Another visual representation of the repository```{bash}#| label: gitDraw9a#| engine: bash#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit9a.png ```![](10_git_files/figure-html/drawGit9a.png){width=100%}:::If we now make a change (such as a change to file1 and adding file3)and commit, it would be as if any commits after `r commits$hash[2]`had never occurred.```{bash}#| label: git-reset9d#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1echo'End'> file1echo'File3'>> dir1/file3git add file1 dir1/file3git commit -m'Modified file1 and added file3'```::: {.callout-note collapse="true"}## More information about this repository```{r, replace=TRUE}#| label: git-rollback4a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all``````{r, replace=TRUE}#| label: git-rollback4b#| echo: !expr -1#| eval: false#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reflog``````{r, replace=TRUE}#| label: git-rollback4c#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset ascii```Notice the addition of `file3` in `dir1````{r, replace=TRUE}#| label: git-rollback4d#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git ls-files```:::```{cat}#| label: Fig9c#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig9c.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash7}{color_commit}{commitshash7}{commitscomment7}\draw [<-,line width=3pt,draw=black!60] (commitshash2) -- (commitshash7.west);\commitz{below = 3cm of commitshash7}{commitshash3}{color_zombie}{commitshash3}{commitscomment3}{black!20!white}\draw [<-,line width=3pt,draw=black!20] (commitshash2.east) -- (commitshash3.west);\master{below = 1cm of commitshash7} \HEAD{below = 0.1cm of master} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='asis'}library(knitr)commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig9c.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig9c.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig9c.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig9c.pdf ../resources/Fig9c.png")```![](../resources/Fig9c.png)::: {.callout-note collapse="true"}## Another visual representation of the repository```{bash}#| label: gitDraw9c#| engine: bash#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit9c.png ```![](10_git_files/figure-html/drawGit9c.png){width=100%}:::## RevertAs with `git reset`, `git revert` is not directly supported byRstudio, hence the methods used in this section should be performed inthe terminal. There is one exception to this, Rstudio is able torevert an modified file back to its state in the last commit.::: {.callout-note collapse="true"}## Re-establish repository```{r, replace=TRUE}#| label: git-reset3a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1```:::```{r, cache=FALSE, echo=FALSE, results='markdown'}library(knitr)commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub(".*:", "", commits$comment), 15), "..")```Revert generates a new commit that removes the changes that wereintroduced by one or more of the most recent commits. Note, it doesnot revert to a particular commit, but rather undoes a commit. So, toroll back to ``r commits$hash[2]`` (the secondlast commit), we just have to revert the last commit(`HEAD`).```{bash}#| label: git-revert1a#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1git revert HEAD```However, if we explore the `reflog`, we can see the entire history```{r, replace=TRUE}#| label: git-rollback5b#| echo: !expr -1#| eval: false#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reflog``````{r, replace=TRUE}#| label: git-rollback5a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all```::: {.callout-note collapse="true"}## More information about this repository```{r, replace=TRUE}#| label: git-rollback5c#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset ascii```Notice the absence of `.gitignore`. Notice also that `dir1/f.tmp` isalso present. Although this file was added at the same time as`.gitignore`, it was never committed and therefore is not altered withrepo manipulations.If we list the files that are part of the repo:```{r, replace=TRUE}#| label: git-rollback5d#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git ls-files```we will see that we are back to the state where only `file1` and`dir1/file2` are present.:::```{cat}#| label: Fig9d#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig9d.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_commit!20}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2) -- (commitshash3.west);\commit{right = 1.5cm of commitshash3}{commitshash7}{color_commit}{commitshash7}{commitscomment7}\draw [<-,line width=3pt,draw=black!60] (commitshash3.east) -- (commitshash7.west);\master{below = 1cm of commitshash7} \HEAD{below = 0.1cm of master} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='markdown'}library(knitr)commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig9d.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig9d.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig9d.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig9d.pdf ../resources/Fig9d.png")```![](../resources/Fig9d.png)::: {.callout-note collapse="true"}## Another visual representation of the repository```{bash}#| label: gitDraw9d#| engine: bash#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit9d.png ```![](10_git_files/figure-html/drawGit9d.png){width=100%}:::::: {.callout-note collapse="true"}## Re-establish repository```{r, replace=TRUE}#| label: git-reset3a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1```:::If we had actually wanted to roll back to commit ``r commits$hash[1]``, then we could do so by sequentially issuing `git revert`:```{bash}#| label: git-revert2a#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1git revert --no-commit HEADgit revert --no-commit HEAD~1git commit -m'Rolled back'```::: {.callout-note collapse="true"}## More information about this repository```{r, replace=TRUE}#| label: git-rollback6b#| echo: !expr -1#| eval: false#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reflog``````{r, replace=TRUE}#| label: git-rollback6a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all``````{r, replace=TRUE}#| label: git-rollback6c#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset ascii```Notice that `file2` is now also absent. If we list the files that arepart of the repo:```{r, replace=TRUE}#| label: git-rollback6d#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git ls-files```we will see that we are back to the state where only `file` is present:::```{cat}#| label: Fig9e#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig9e.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_commit!20}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2) -- (commitshash3.west);\commit{right = 1.5cm of commitshash3}{commitshash7}{color_commit}{commitshash7}{commitscomment7}\draw [<-,line width=3pt,draw=black!60] (commitshash3.east) -- (commitshash7.west);\master{below = 1cm of commitshash7} \HEAD{below = 0.1cm of master} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='markdown'}library(knitr)commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig9e.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig9e.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig9e.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig9e.pdf ../resources/Fig9e.png")```![](../resources/Fig9e.png)::: {.callout-note collapse="true"}## Another visual representation of the repository```{bash}#| label: gitDraw9e#| engine: bash#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit9e.png ```![](10_git_files/figure-html/drawGit9e.png){width=100%}:::## Checkout and branchingOnce again, the methods outlined in this section are not directlysupported by Rstudio. Please use the terminal instead.::: {.callout-note collapse="true"}## Re-establish repository```{r, replace=TRUE}#| label: git-reset3a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1```:::If we wanted to review the state of files corresponding to commit ``r commits$hash[2]``, we could checkout the code from that commit. Thisprovides a way to travel back in time through your commits and explorethe (tracked) files exactly as they were.```{r}#| label: bash3#| echo: false#| eval: true#| cache: falsehash =read.fwf('~/tmp/Repo1/.git/logs/HEAD', widths=40)hash_commit =strtrim(hash$V1[3], width=5)knit_engines$set(bash1=function(options) { engine=options$engine options$code =gsub('#',hash_commit,options$code) code =paste("-c",shQuote(paste(options$code, collapse="\n"))) code =paste(options$engine.opts,code) cmd ='bash' out =system2(cmd, code, stdout=TRUE, stderr=TRUE, env=options$engine.env)engine_output(options,options$code,out) })``````{r, replace=TRUE}#| label: git-reset9e#| echo: !expr -1#| eval: true#| cache: false#| warning: true#| message: true#| error: true#| classes: bash#| engine: bash1cd ~/tmp/Repo1git checkout #```::: {.callout-note collapse="true"}## More information about this repository```{r, replace=TRUE}#| label: git-rollback7b#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reflog``````{r, replace=TRUE}#| label: git-rollback7a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all``````{r, replace=TRUE}#| label: git-rollback7c#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1tree -ra -L 2 --charset ascii```Notice that `file2` is now also absent. If we list the files that arepart of the repo:```{r, replace=TRUE}#| label: git-rollback7d#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git ls-files```we will see that we are back to the state where only `file` is present:::```{cat}#| label: Fig9f#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig9f.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_commit!20}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_commit}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2) -- (commitshash3.west);\master{below = 1cm of commitshash3} \HEAD{below = 1cm of commitshash2} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='markdown'}library(knitr)commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig9f.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig9f.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig9f.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig9f.pdf ../resources/Fig9f.png")```![](../resources/Fig9f.png)::: {.callout-note collapse="true"}## Another visual representation of the repository```{bash}#| label: gitDraw9f#| engine: bash#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit9f.png ```![](10_git_files/figure-html/drawGit9f.png){width=100%}:::::: {.callout-note collapse="true"}## Rstudio git history representationIf we go to the "History" tab of the "Review Changes" window, you willnotice that the commit history has been truncated to reflect that wehave gone back in commit history.![](../resources/rstudio_gitrollback2.png)Nevertheless, if we select "All branches" from the dropdown menu, wecan see the full commit history.![](../resources/rstudio_gitrollback3.png):::The output advises us that we are in a **detached HEAD state**.This occurs when a `commit` is checked out rather than a`branch`. Normally, when changes are committed, the newcommit is added to the `HEAD` of the current branch.However, in a detached HEAD state, any commits that are made are notassociated with any branch and will effectively be lost next time youcheckout.So if for example, we then added another file (`file3`)..```{bash}#| label: git-checkout9#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1echo'END'> file3git add file3git commit -m'END added to file3'```::: {.callout-note collapse="true"}## More information about this repository```{r, replace=TRUE}#| label: git-checkout9b#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reflog``````{r, replace=TRUE}#| label: git-checkout9a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all```:::Now if we checked out `main`, the commit we made whilst in _detachedhead mode_ would be lost.```{bash}#| label: git-checkout10#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1git checkout main```::: {.callout-note collapse="true"}## More information about this repository```{r, replace=TRUE}#| label: git-checkout9c#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reflog``````{r, replace=TRUE}#| label: git-checkout9d#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all```:::If, having reviewed the state of a commit (by checking it out), wedecided that we wanted to roll back to this state and develop further(make additional commits), we are effectively deciding to start a newbranch that splits off at that commit. See the section on <ahref="#Branching">Branching</a> for more details on how to do that.# Synching with remote repositoryWhen a project has multiple contributors, it is typical for there tobe a remote repository against which each contributor can exchangetheir contributions. The remote repository comprises only the `.git`folder (and its contents), it never has a workspace. Files are rarelyedited directly on the remote repository. Instead, it acts as aconstantly available 'main' conduit between all contributors.A remote repository can be anywhere that you have permission to atleast read from. Obviously, if you also want to contribute your localcommits to the remote repository, you also need write access to thatlocation. If you intend to collaborate, then the remote repositoryalso needs to be in a location that all users can access at any time.For this demonstration, we will start by re-generating a repositorythat we made earlier on in this tutorial. This repository comprises a`main` branch along with an un-merged `Feature` branch.::: {.callout-note collapse="true"}## Commands to create the repository```{bash}#| label: git_status10#| classes: bash#| engine: bash#| cache: false#| echo: true#| eval: truerm-rf ~/tmp/Repo1mkdir ~/tmp/Repo1cd ~/tmp/Repo1git init echo'File 1'> file1git add file1git commit -m'Initial repo and added file1'echo'---------------'>> file1mkdir dir1echo'* Notes'> dir1/file2git add file1 dir1/file2git commit -m'Modified file1 and added file2 (in dir1)'echo'---'>> dir1/file2echo'temp'> dir1/f.tmpecho'*.tmp'> .gitignoregit add .git commit -m'Modified file2, added .gitignore'git branch Featuregit checkout Featureecho'b'>> file1echo'File 3'> dir1/file3git add .git commit -m'New feature'git checkout mainecho' another bug fix'>> dir1/file2git add .git commit -m'Bug fix in file1'git checkout Featureecho' a modification'>> dir1/file3git add .git commit -m'Feature complete'git checkout maingit refloggit log --graph--decorate--oneline--all```:::```{cat}#| label: Fig10a#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig10a.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);% new commit on main\commit{right = 1.5cm of commitshash3}{commitshash5}{color_commit}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (commitshash3.east) -- (commitshash5.west);\commit{above = 3cm of commitshash5}{commitshash4}{color_inactive}{commitshash4}{commitscomment4}\draw [<-,line width=3pt,draw=black!60] (commitshash3.north east) -- (commitshash4.south west);\commit{right = 1.5cm of commitshash4}{commitshash6}{color_inactive}{commitshash6}{commitscomment6}\draw [<-,line width=3pt,draw=black!60] (commitshash4.east) -- (commitshash6.west);\branch{below = 1cm of commitshash6}{Feature} \master{below = 1cm of commitshash5} \HEAD{below = 0.1cm of master} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='markup'}library(knitr)## commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)## commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')## commits = commits[rev(1:nrow(commits)),]## colnames(commits) <- c('hash','comment')## commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')## lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)## lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')## lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))commits = system("cd ~/tmp/Repo1; git log --oneline --all | awk '{ print $1,\"\\t\",substr($0, 9) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),10),'..') #print(commits)# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig10a.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig10a.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig10a.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig10a.pdf ../resources/Fig10a.png")```![](../resources/Fig10a.png)## Simulate a remote repository locallyFor the purpose of this tutorial, we will create a remote repositorythat is on the same computer as the above repository that we have beenworking on. Whilst not the typical situation, it does mean that anexternal location and account is not necessary to follow along withthe tutorial. As previously mentioned, the actual location of theremote repository is almost irrelevant to how you interact with it.Therefore, whether the remote repository is on the same computer orelsewhere in the world makes little difference (other than permissionsand connections).This step is not supported directly by Rstudio - please use the terminal.```{R}#| label: makedirectory2#| cache: false#| echo: falseunlink('~/tmp/RemoteRepo1', recursive=TRUE, force=TRUE)``````{bash}#| label: git-push1a#| echo: !expr -1#| eval: true#| cache: false#| classes: bashmkdir ~/tmp/RemoteRepo1cd ~/tmp/RemoteRepo1git init --bare```Now that we have a remote repository - albeit empty at this stage - wereturn to our local repository and declare (add) the location of theremote repository using the `git remote add <name> <url>` _command_.In this command, an optional name can be supplied to refer to theremote repository (`<name>`). The compulsory `<url>` _argument_ is theaddress (location) of the remote repository.This step is not supported directly by Rstudio - please use theterminal.```{bash}#| label: git-push1b#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1git remote add origin ~/tmp/RemoteRepo1```To see what this has achieved, we can have a quick look at the`.git/config````{bash}#| label: git-push1c#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1cat .git/config```You should notice that there is now a 'remote' section with the nameof 'origin' and the 'url' points to the location we nominated.### PushingCurrently the remote repository is empty. We will now push our localcommit history to the remote repository. This is achieved via the `gitpush -u <name> <ref>` <i>command</i>. Here, `<name>` is the name ofthe remote repository ('origin') and `<ref>` is a reference the headof the commit chain we want to sync.::: {.panel-tabset}#### Terminal```{bash}#| label: git-push1d#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1git push -u origin main```#### RstudioRstudio does not have a direct means by which we can define the remoterepository. Thus, we must start by entering the following into theterminal.```{bash}#| label: git-push1d#| echo: !expr -1#| eval: false#| cache: false#| classes: bash```Thereafter, you might notice that some up and down (push and pullrespectively) buttons become active within the "git" panel.![](../resources/rstudio_gitpull1.png)Now, after each subsequent commit, you can "push" your code to theremote repository simply by pushing the up (push) arrow.:::```{bash}#| label: git-push1e#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1git refloggit log --graph--decorate--oneline--all``````{cat}#| label: Fig10b#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig10b.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);% new commit on main\commit{right = 1.5cm of commitshash3}{commitshash5}{color_commit}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (commitshash3.east) -- (commitshash5.west);\commit{above = 3cm of commitshash5}{commitshash4}{color_inactive}{commitshash4}{commitscomment4}\draw [<-,line width=3pt,draw=black!60] (commitshash3.north east) -- (commitshash4.south west);\commit{right = 1.5cm of commitshash4}{commitshash6}{color_inactive}{commitshash6}{commitscomment6}\draw [<-,line width=3pt,draw=black!60] (commitshash4.east) -- (commitshash6.west);\branch{below = 1cm of commitshash6}{Feature} \master{below = 1cm of commitshash5} \HEAD{below = 0.1cm of master} \node [anchor=north west,TARGET] at ($(commitshash1.south) + (0,-2cm)$) {Local Repository};\rcommit{right = 8cm of commitshash5}{rcommitshash1}{color_inactive}{commitshash1}{commitscomment1}\rcommit{right = 1.5cm of rcommitshash1}{rcommitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (rcommitshash1) -- (rcommitshash2.west);\rcommit{right = 1.5cm of rcommitshash2}{rcommitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (rcommitshash2) -- (rcommitshash3.west);% new commit on main\rcommit{right = 1.5cm of rcommitshash3}{rcommitshash5}{color_commit}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (rcommitshash3.east) -- (rcommitshash5.west);%\rcommit{above = 3cm of rcommitshash5}{rcommitshash4}{color_inactive}{commitshash4}{commitscomment4}%\draw [<-,line width=3pt,draw=black!60] (rcommitshash3.north east) -- (rcommitshash4.south west);%\rcommit{right = 1.5cm of rcommitshash4}{rcommitshash6}{color_inactive}{commitshash6}{commitscomment6}%\draw [<-,line width=3pt,draw=black!60] (rcommitshash4.east) -- (rcommitshash6.west);%\rbranch{below = 1cm of rcommitshash6}{Feature} \rmaster{below = 1cm of rcommitshash5} \rHEAD{below = 0.1cm of rmaster} %\rbranch{below = 1cm of rcommitshash6}{Feature} \node [anchor=north west,TARGET] at ($(rcommitshash1.south) + (0,-2cm)$) (Remote) {Remote Repository};\draw [dashed] ($(rcommitshash1.west |- rcommitshash1.north west) +(-0.5cm,0.5cm)$) rectangle ($(rHEAD.south east) +(0.8cm,-0.5cm)$);\draw [->,very thick,dashed] ($(commitshash5.east) +(0.5cm,0)$) to [out=10,in=220] ($(rcommitshash1.east) +(-2cm,0)$);\end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='markdown'}library(knitr)#commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)#commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')#commits = commits[rev(1:nrow(commits)),]#colnames(commits) <- c('hash','comment')#commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')#lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)#lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')#lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))commits = system("cd ~/tmp/Repo1; git log --oneline --all | awk '{ print $1,\"\\t\",substr($0, 9) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),10),'..') #print(commits)# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig10b.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig10b.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig10b.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig10b.pdf ../resources/Fig10b.png")```![](../resources/Fig10b.png)::: {.callout-note collapse="true"}## Another visual representation of the repository```{bash}#| label: gitDraw10b#| engine: bash#| echo: false./../resources/git-draw--image-only--sha1-length 5 --hide-legend--hide-reflogs--git-dir ~/tmp/Repo1/.git --image-filename 10_git_files/figure-html/drawGit10b.png ```![](10_git_files/figure-html/drawGit10b.png){width=100%}:::**Note that when we pushed the commits to the remote repository, we only pushed the `main` branch. Consequently, the remote repository only has a single branch.**## CloningTo collaborate with others on a repository, we start by **cloning**the repository you wish to collaborate on. So at the moment, we havethe original repository (`~/tmp/Repo1`) created by user 1. We alsohave a remote repository (`~/tmp/RemoteRepo1`). To demonstrate cloning (and collaborating), we will also assume thepersonal of user 2 and we will clone the remote repository to yetanother local path (`~/tmp/MyRepo1`). Of course, this would notnormally be on the same machine as the original repository, we arejust doing it this way to simulate multiple users on the same machine.```{R}#| label: makedirectory3#| cache: false#| echo: falseunlink('~/tmp/MyRepo1', recursive=TRUE, force=TRUE)```::: {.panel-tabset}#### Terminal```{bash}#| label: git-clone1a#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1git clone ~/tmp/RemoteRepo1 ~/tmp/MyRepo1```#### Rstudio1. click on the `Project` selector in the top right of the Rstudio window (as highlighted by the red ellipse in the image below.![](../resources/rstudio_init1a.png){width=100%}2. select `New Project` from the dropdown menu3. select `Version Control` form the Create Project panel4. select `Git` from the Create Project from Version Control panel5. provide a path to a remote repository. Normally this URL would be for a location on a server such as Github, Gitlab, Bitbucket etc. However, for this demostration we will point to the remote repository that we set up in the previous section (`~/tmp/RemoteRepo1`)6. provide a directory name in which to store this new cloned repository. Normally this field is populated based on the name give in the URL. However, in this case, it would suggest a name of `RemoteRepo1` which already exists (for the repository we are trying to clone), and we don't wish to overwrite that one. I will instead offer an alternative name (`MyRepo1`).7. we also need to supply a path to where this cloned repository will be stored.![](../resources/rstudio_gitclone1.png)8. click the "Create Project" button. :::The contents (and state) of `~/tmp/MyRepo1` should match that of`~/tmp/Repo1` (other than any files excluded due to a `.gitignore` orfiles not yet committed).```{r, replace=TRUE}#| label: git-clone1b#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/MyRepo1tree -ra -L 2 --charset ascii``````{cat}#| label: Fig10c#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig10c.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);% new commit on main\commit{right = 1.5cm of commitshash3}{commitshash5}{color_commit}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (commitshash3.east) -- (commitshash5.west);\commit{above = 3cm of commitshash5}{commitshash4}{color_inactive}{commitshash4}{commitscomment4}\draw [<-,line width=3pt,draw=black!60] (commitshash3.north east) -- (commitshash4.south west);\commit{right = 1.5cm of commitshash4}{commitshash6}{color_inactive}{commitshash6}{commitscomment6}\draw [<-,line width=3pt,draw=black!60] (commitshash4.east) -- (commitshash6.west);\branch{below = 1cm of commitshash6}{Feature} \master{below = 1cm of commitshash5} \HEAD{below = 0.1cm of master} \node [anchor=north west,TARGET] at ($(commitshash1.south) + (0,-2cm)$) {Local Repository};\draw [fill=white, fill opacity = 0.5, draw=none] ($(commitshash1.west |- commitshash6.north west) +(-0.5cm,0.5cm)$) rectangle ($(commitshash6.east |- HEAD.south east) +(0.8cm,-0.5cm)$);\rcommit{right = 8cm of commitshash5}{rcommitshash1}{color_inactive}{commitshash1}{commitscomment1}\rcommit{right = 1.5cm of rcommitshash1}{rcommitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (rcommitshash1) -- (rcommitshash2.west);\rcommit{right = 1.5cm of rcommitshash2}{rcommitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (rcommitshash2) -- (rcommitshash3.west);% new commit on main\rcommit{right = 1.5cm of rcommitshash3}{rcommitshash5}{color_commit}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (rcommitshash3.east) -- (rcommitshash5.west);%%\rcommit{above = 3cm of rcommitshash5}{rcommitshash4}{color_inactive}{commitshash4}{commitscomment4}%%\draw [<-,line width=3pt,draw=black!60] (rcommitshash3.north east) -- (rcommitshash4.south west);%%\rcommit{right = 1.5cm of rcommitshash4}{rcommitshash6}{color_inactive}{commitshash6}{commitscomment6}%%\draw [<-,line width=3pt,draw=black!60] (rcommitshash4.east) -- (rcommitshash6.west);%%\rbranch{below = 1cm of rcommitshash6}{Feature} \rmaster{below = 1cm of rcommitshash5} \rHEAD{below = 0.1cm of rmaster} \node [anchor=north west,TARGET] at ($(rcommitshash1.south) + (0,-2cm)$) (Remote) {Remote Repository};\draw [dashed] ($(rcommitshash1.west |- rcommitshash1.north west) +(-0.5cm,0.5cm)$) rectangle ($(rHEAD.south east) +(0.8cm,-0.5cm)$);%\draw [->,very thick,dashed] ($(commitshash5.east) +(0.5cm,0)$) to [out=10,in=220] ($(rcommitshash1.east) +(-2cm,0)$);\commit{below = 5cm of commitshash1}{commits2hash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commits2hash1}{commits2hash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commits2hash1) -- (commits2hash2.west);\commit{right = 1.5cm of commits2hash2}{commits2hash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commits2hash2.east) -- (commits2hash3.west);% new commit on main\commit{right = 1.5cm of commits2hash3}{commits2hash5}{color_commit}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (commits2hash3.east) -- (commits2hash5.west);%\commit{above = 3cm of commits2hash5}{commits2hash4}{color_inactive}{commitshash4}{commitscomment4}%\draw [<-,line width=3pt,draw=black!60] (commits2hash3.north east) -- (commits2hash4.south west);%\commit{right = 1.5cm of commits2hash4}{commits2hash6}{color_inactive}{commitshash6}{commitscomment6}%\draw [<-,line width=3pt,draw=black!60] (commits2hash4.east) -- (commits2hash6.west);%\branch{below = 1cm of commitshash6}{Feature} \master{below = 1cm of commits2hash5} \HEAD{below = 0.1cm of master} \node [anchor=north west,TARGET] at ($(commits2hash1.south) + (0,-2cm)$) {Cloned Repository};\draw [<-,very thick,dashed] ($(commits2hash5.east) +(0.5cm,0)$) to [out=10,in=220] ($(rcommitshash1.east) +(-2cm,0)$);\end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='markdown'}library(knitr)#commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)#commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')#commits = commits[rev(1:nrow(commits)),]#colnames(commits) <- c('hash','comment')#commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')#lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)#lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')#lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))commits = system("cd ~/tmp/Repo1; git log --oneline --all | awk '{ print $1,\"\\t\",substr($0, 9) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),10),'..') #print(commits)# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig10c.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig10c.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig10c.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig10c.pdf ../resources/Fig10c.png")```![](../resources/Fig10c.png)**Note that when cloning repository, all branches in the remote repository are cloned. However, since the remoterepository only had one branch (`main`), so too the cloneonly has one branch.**Now as the collaborator (user 2), lets make a modification and pushthis change up to the remote repository.::: {.callout-important collapse="false"}## Important info about pushing to a remote repository Before pushing any changes, it is absolutely vital that you adhere tothe following steps:1. commit your changes - so that you have something new to push and they are safe before the next step.2. pull (and if necessary reconcile - see the next section below) the latest from the remote repository. **This is critical** as it ensures that the changes you are pushing are against the latest stage of the repository. Without this step, you might be pushing changes that are based on a stage that is not longer current.3. push your changes:::::: {.panel-tabset}#### Terminal```{bash}#| label: git-clone2a#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/MyRepo1git pull``````{bash}#| label: git-clone2b#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/MyRepo1echo'Something else'> file4git add file4git commit -m'Added file4'``````{bash}#| label: git-clone2c#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/MyRepo1git push -u origin main```#### Rstudio1. start by pulling the latest from the remote repository just incase there has been an change2. click on the "Create new blank file in the current directory" button and select "Text file" - name it `file4`3. edit this file by adding the contents `Something else`4. save the file5. stage (add) the file6. commit the change with a message of "Added file4"7. push this commit either by clicking on the green up (push) arrow in the "Review Changes" window or the same arrow in the git tab of the main Rstudio window.:::```{cat}#| label: Fig10d#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig10d.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);% new commit on main\commit{right = 1.5cm of commitshash3}{commitshash5}{color_commit}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (commitshash3.east) -- (commitshash5.west);\commit{above = 3cm of commitshash5}{commitshash4}{color_inactive}{commitshash4}{commitscomment4}\draw [<-,line width=3pt,draw=black!60] (commitshash3.north east) -- (commitshash4.south west);\commit{right = 1.5cm of commitshash4}{commitshash6}{color_inactive}{commitshash6}{commitscomment6}\draw [<-,line width=3pt,draw=black!60] (commitshash4.east) -- (commitshash6.west);\branch{below = 1cm of commitshash6}{Feature} \master{below = 1cm of commitshash5} \HEAD{below = 0.1cm of master} \node [anchor=north west,TARGET] at ($(commitshash1.south) + (0,-2cm)$) {Local Repository};\draw [fill=white, fill opacity = 0.5, draw=none] ($(commitshash1.west |- commitshash6.north west) +(-0.5cm,0.5cm)$) rectangle ($(commitshash6.east |- HEAD.south east) +(0.8cm,-0.5cm)$);\rcommit{right = 8cm of commitshash5}{rcommitshash1}{color_inactive}{commitshash1}{commitscomment1}\rcommit{right = 1.5cm of rcommitshash1}{rcommitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (rcommitshash1) -- (rcommitshash2.west);\rcommit{right = 1.5cm of rcommitshash2}{rcommitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (rcommitshash2) -- (rcommitshash3.west);% new commit on main\rcommit{right = 1.5cm of rcommitshash3}{rcommitshash4}{color_inactive}{commitshash4}{commitscomment4}\draw [<-,line width=3pt,draw=black!60] (rcommitshash3.east) -- (rcommitshash4.west);\rcommit{right = 1.5cm of rcommitshash4}{rcommitshash5}{color_commit}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (rcommitshash4.east) -- (rcommitshash5.west);%%\rcommit{above = 3cm of rcommitshash5}{rcommitshash4}{color_inactive}{commitshash4}{commitscomment4}%%\draw [<-,line width=3pt,draw=black!60] (rcommitshash3.north east) -- (rcommitshash4.south west);%%\rcommit{right = 1.5cm of rcommitshash4}{rcommitshash6}{color_inactive}{commitshash6}{commitscomment6}%%\draw [<-,line width=3pt,draw=black!60] (rcommitshash4.east) -- (rcommitshash6.west);%%\rbranch{below = 1cm of rcommitshash6}{Feature} \rmaster{below = 1cm of rcommitshash5} \rHEAD{below = 0.1cm of rmaster} \node [anchor=north west,TARGET] at ($(rcommitshash1.south) + (0,-2cm)$) (Remote) {Remote Repository};\draw [dashed] ($(rcommitshash1.west |- rcommitshash1.north west) +(-0.5cm,0.5cm)$) rectangle ($(rHEAD.south east) +(0.8cm,-0.5cm)$);%\draw [->,very thick,dashed] ($(commitshash5.east) +(0.5cm,0)$) to [out=10,in=220] ($(rcommitshash1.east) +(-2cm,0)$);\commit{below = 5cm of commitshash1}{commits2hash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commits2hash1}{commits2hash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commits2hash1) -- (commits2hash2.west);\commit{right = 1.5cm of commits2hash2}{commits2hash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commits2hash2.east) -- (commits2hash3.west);% new commit on main\commit{right = 1.5cm of commits2hash3}{commits2hash4}{color_inactive}{commitshash4}{commitscomment4}\draw [<-,line width=3pt,draw=black!60] (commits2hash3.east) -- (commits2hash4.west);\commit{right = 1.5cm of commits2hash4}{commits2hash5}{color_commit}{commitshash5}{commitscomment5}\draw [->,line width=3pt,draw=black!60] (commits2hash4.east) -- (commits2hash5.west);%\commit{right = 1.5cm of commits2hash4}{commits2hash6}{color_inactive}{commitshash6}{commitscomment6}%\draw [<-,line width=3pt,draw=black!60] (commits2hash4.east) -- (commits2hash6.west);%\branch{below = 1cm of commitshash6}{Feature} \master{below = 1cm of commits2hash5} \HEAD{below = 0.1cm of master} \node [anchor=north west,TARGET] at ($(commits2hash1.south) + (0,-2cm)$) {Cloned Repository};\draw [<-,very thick,dashed] ($(commits2hash5.east) +(0.5cm,0)$) to [out=10,in=220] ($(rcommitshash1.east) +(-2cm,0)$);\end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='markdown'}library(knitr)#commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)#commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')#commits = commits[rev(1:nrow(commits)),]#colnames(commits) <- c('hash','comment')#commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')#lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)#lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')#lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))commits = system("cd ~/tmp/Repo1; git log --oneline --all | awk '{ print $1,\"\\t\",substr($0, 9) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),10),'..') #print(commits)# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig10d.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig10d.tikz")) }zcommits = system("cd ~/tmp/MyRepo1; git log --oneline --all | awk '{ print $1,\"\\t\",substr($0, 9) }'", intern=TRUE)zcommits = read.table(textConnection(zcommits), header=FALSE, strip.white=TRUE, sep='\t')zcommits = zcommits[rev(1:nrow(zcommits)),]colnames(zcommits) <- c('hash','comment')zcommits$comment <- paste0(strtrim(gsub('.*:','',zcommits$comment),10),'..') #print(zcommits)# get the commits from .git/logs/HEADfor (i in 1:dim(zcommits)[1]) { system(paste0("sed -i 's/commits2hash",i,"\\([\\.\\})]\\)/", zcommits$hash[i], "\\1/g' ../resources/Fig10d.tikz")) system(paste0("sed -i 's/commits2comment",i,"\\([\\.\\})]\\)/", zcommits$comment[i], "\\1/g' ../resources/Fig10d.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig10d.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig10d.pdf ../resources/Fig10d.png")```![](../resources/Fig10d.png)Notice how the second (cloned. `MyRepo1`) repository and the remote repository (`RemoteRepo1`) are one commit ahead of the original local repository (`Repo1`). For `Repo1` to be in syncwith `MyRepo1`, the original user will have to pull the remote repository changes manually.## PullingRetrieving a commit chain (_pulling_) from a remote repository issuperficially the opposite of _pushing_. Actually, technically it is twoactions: - a _fetch_ that retrieves the remote information and uses it to create a _branch_ off your local repository (the name of this branch is made from the name of the remote and the branch that was fetched - e.g. `origin/master`).- a _merge_ that merges this branch into the main repository. These actions can be performed individually, however, they are moretypically performed together via the `git pull` _command_. To illustrate, lets return to being user 1 and we will pull thechanges contributed by user 2 in the section above.```{bash}#| label: git-pull1a#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1git pull```The associated message informs us that upon pulling, a file (`file4`)has been added. Any conflicts arising from the _merging_ stage of thepull can be resolved in the usual manner of opening the conflictedfile(s) making manual edits and then committing the changes.```{bash}#| label: git-pull3a#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1git reflog``````{bash}#| label: git-pull3b#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/Repo1git log --graph--decorate--oneline--all``````{bash}#| label: git-pull3c#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/MyRepo1git reflog``````{bash}#| label: git-pull3d#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/MyRepo1git log --graph--decorate--oneline--all``````{bash}#| label: git-pull3e#| echo: !expr -1#| eval: true#| cache: false#| classes: bashcd ~/tmp/RemoteRepo1git refloggit log --graph--decorate--oneline--all``````{cat}#| label: Fig10e#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig10e.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);% new commit on main\commit{right = 1.5cm of commitshash3}{commitshash5}{color_inactive}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (commitshash3.east) -- (commitshash5.west);\commit{above = 3cm of commitshash5}{commitshash4}{color_inactive}{commitshash4}{commitscomment4}\draw [<-,line width=3pt,draw=black!60] (commitshash3.north east) -- (commitshash4.south west);\commit{right = 1.5cm of commitshash4}{commitshash6}{color_inactive}{commitshash6}{commitscomment6}\draw [<-,line width=3pt,draw=black!60] (commitshash4.east) -- (commitshash6.west);\branch{below = 1cm of commitshash6}{Feature} \commit{right = 1.5cm of commitshash5}{commitshash7}{color_commit}{commits2hash5}{commits2comment5}\draw [<-,line width=3pt,draw=black!60] (commitshash5.east) -- (commitshash7.west);\master{below = 1cm of commitshash7} \HEAD{below = 0.1cm of master} \node [anchor=north west,TARGET] at ($(commitshash1.south) + (0,-2cm)$) {Local Repository};%\draw [fill=white, fill opacity = 0.5, draw=none] ($(commitshash1.west |- commitshash6.north west) +(-0.5cm,0.5cm)$) rectangle ($(commitshash6.east |- HEAD.south east) +(0.8cm,-0.5cm)$);\rcommit{right = 8cm of commitshash5}{rcommitshash1}{color_inactive}{commitshash1}{commitscomment1}\rcommit{right = 1.5cm of rcommitshash1}{rcommitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (rcommitshash1) -- (rcommitshash2.west);\rcommit{right = 1.5cm of rcommitshash2}{rcommitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (rcommitshash2) -- (rcommitshash3.west);% new commit on main\rcommit{right = 1.5cm of rcommitshash3}{rcommitshash5}{color_inactive}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (rcommitshash3.east) -- (rcommitshash5.west);\rcommit{right = 1.5cm of rcommitshash5}{rcommitshash6}{color_commit}{commits2hash5}{commits2comment5}\draw [<-,line width=3pt,draw=black!60] (rcommitshash5.east) -- (rcommitshash6.west);%%\rcommit{above = 3cm of rcommitshash5}{rcommitshash4}{color_inactive}{commitshash4}{commitscomment4}%%\draw [<-,line width=3pt,draw=black!60] (rcommitshash3.north east) -- (rcommitshash4.south west);%%\rcommit{right = 1.5cm of rcommitshash4}{rcommitshash6}{color_inactive}{commitshash6}{commitscomment6}%%\draw [<-,line width=3pt,draw=black!60] (rcommitshash4.east) -- (rcommitshash6.west);%%\rbranch{below = 1cm of rcommitshash6}{Feature} \rmaster{below = 1cm of rcommitshash6} \rHEAD{below = 0.1cm of rmaster} \node [anchor=north west,TARGET] at ($(rcommitshash1.south) + (0,-2cm)$) (Remote) {Remote Repository};\draw [dashed] ($(rcommitshash1.west |- rcommitshash1.north west) +(-0.5cm,0.5cm)$) rectangle ($(rHEAD.south east) +(0.8cm,-0.5cm)$);%\draw [->,very thick,dashed] ($(commitshash5.east) +(0.5cm,0)$) to [out=10,in=220] ($(rcommitshash1.east) +(-2cm,0)$);\draw [<-,very thick,dashed] ($(commitshash7.east) +(0.5cm,0)$) to [out=10,in=220] ($(rcommitshash1.east) +(-2cm,0)$);\commit{below = 5cm of commitshash1}{commits2hash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commits2hash1}{commits2hash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commits2hash1) -- (commits2hash2.west);\commit{right = 1.5cm of commits2hash2}{commits2hash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commits2hash2.east) -- (commits2hash3.west);% new commit on main\commit{right = 1.5cm of commits2hash3}{commits2hash4}{color_inactive}{commitshash4}{commitscomment4}\draw [<-,line width=3pt,draw=black!60] (commits2hash3.east) -- (commits2hash4.west);\commit{right = 1.5cm of commits2hash4}{commits2hash5}{color_commit}{commitshash5}{commitscomment5}\draw [->,line width=3pt,draw=black!60] (commits2hash4.east) -- (commits2hash5.west);%\commit{right = 1.5cm of commits2hash4}{commits2hash6}{color_inactive}{commitshash6}{commitscomment6}%\draw [<-,line width=3pt,draw=black!60] (commits2hash4.east) -- (commits2hash6.west);%\branch{below = 1cm of commitshash6}{Feature} \master{below = 1cm of commits2hash5} \HEAD{below = 0.1cm of master} \node [anchor=north west,TARGET] at ($(commits2hash1.south) + (0,-2cm)$) {Cloned Repository};%\draw [<-,very thick,dashed] ($(commits2hash5.east) +(0.5cm,0)$) to [out=10,in=220] ($(rcommitshash1.east) +(-2cm,0)$);\draw [fill=white, fill opacity = 0.5, draw=none] ($(commits2hash1.west |- commits2hash5.north west) +(-0.5cm,0.5cm)$) rectangle ($(commits2hash5.east |- HEAD.south east) +(0.8cm,-0.5cm)$);\end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='markdown'}library(knitr)#commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)#commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')#commits = commits[rev(1:nrow(commits)),]#colnames(commits) <- c('hash','comment')#commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')#lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)#lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')#lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))commits = system("cd ~/tmp/Repo1; git log --oneline --all | awk '{ print $1,\"\\t\",substr($0, 9) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),10),'..') #print(commits)# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig10e.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig10e.tikz")) }zcommits = system("cd ~/tmp/MyRepo1; git log --oneline --all | awk '{ print $1,\"\\t\",substr($0, 9) }'", intern=TRUE)zcommits = read.table(textConnection(zcommits), header=FALSE, strip.white=TRUE, sep='\t')zcommits = zcommits[rev(1:nrow(zcommits)),]colnames(zcommits) <- c('hash','comment')zcommits$comment <- paste0(strtrim(gsub('.*:','',zcommits$comment),10),'..') #print(zcommits)# get the commits from .git/logs/HEADfor (i in 1:dim(zcommits)[1]) { system(paste0("sed -i 's/commits2hash",i,"\\([\\.\\})]\\)/", zcommits$hash[i], "\\1/g' ../resources/Fig10e.tikz")) system(paste0("sed -i 's/commits2comment",i,"\\([\\.\\})]\\)/", zcommits$comment[i], "\\1/g' ../resources/Fig10e.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig10e.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig10e.pdf ../resources/Fig10e.png")```![](../resources/Fig10e.png)## Github as a remote repositoryGitHub provides the world's leading centralized platform for version control, collaboration, and project management, facilitating seamlessteamwork, tracking changes, and ensuring the integrity and accessibilityof code repositories throughout the software development lifecycle.Although anyone can explore (read) public repositories on github, only those with github accounts can contribute and collaborate.### Setup Github accountTo create a **free** github account:1. visit <https://github.com> and click "Sign up for github"2. register by providing your prefered email address, a username and a password when prompted3. to complete the account activation, you will need to verify your details via an email sent to your nominated email addressAs of the start of 2024, github now requires Two-Factor Authentication(2FA) for enhanced security. Whenever you login to github (or areprompted for a password, you will also need to use 2FA. To setup 2FA:1. click on your profile picture in the top right corner.2. select "Settings" from the dropdown menu.3. select "Password and authentication" in the left sidebar.4. under "Two-factor authentication" section, click "Enable".5. choose your preferred method (authenticator app or SMS) and follow the prompts to set it up.Passwords and Two-Factor Authentication (2FA) are used when you (as ahuman) securely login and interact directly with the GitHub website.However, it is also possible to have other tools (such as `git`)interact with Github on your behalf via an Application ProgrammingInterfacet (API). Passwords/2FA are not appropriate to authenticate these machine to machine communications. Instead, Github requires theuse of a Personal Access Token (PAT). PATs offer a more secure and granular approach, allowing users to control access without exposing theiraccount password.To generate a Personal Access Token (PAT):1. click on your profile picture in the top right corner.2. select "Settings" from the dropdown menu.3. select "Developer settings" from the bottom of the left sidebar.4. select "Personal access tokens" from the left sidebar.5. select "Tokens (classic)" from the dropdown menu6. click "Generate new token"7. select "Generate new token (classic)" from the dropdown menu8. at this point you will likely be prompted for your password9. provide a "note" - this is more of a short description of what the token is to be used for (in the example below, I have entered "git push/pull" to remind me that this is a simple token for regular push/pull interaction between my local and remote repositories).![](../resources/rstudio_githubtoken1.png) You also need to provide an expiration. Although not secure or recommended, I have selected "No expiration" as I don't want to have to re-do my PAT across multiple machines too regularly. Finally, you also need to indicate **scope** (what activities you are granting permission for the tools to be able to perform). In this case, I have ticked the "repo" box. This grants general rea/write access to my repositories. I have not granted permission for more administration like activities such as managing teams, deleting repositories, etc - these activities I am happy to perform myself via the website.10. click "Generate token" and securely copy the generated token. Until this is stored safely (see below) do not close the page, because Github will never show you this PAT again.:::: {.indented}::: {.callout-important collapse="false"}Important: Store your PAT safely as you won't be able to see it again!Ideally, you should store this PAT in a digital wallet. Digitalwallets vary according to operating systems. R users might like to usethe `r` function from the `asdf` package (which you will need toinstall prior) as follows in order to store the PAT.**In an R console, enter:**```{r}#| label: PAT#| cache: false#| echo: true#| eval: falsegitcreds::gitcreds_set()```When propted for a password, paste in the copied PAT that hopefully is still in yourclipboard - else you might need to re-copy it.To confirm that you have successfully stored your PAT in your wallet, you can:```{r}#| label: PAT2#| cache: false#| echo: true#| eval: falsegitcreds::gitcreds_get()```and confirm that it indicates that there is a hidden password.:::::::### Create remote Github repository1. login to your Github account2. either: a. click on the "Create new.." button (with the plus sign) to the right of your profile picture in the top right corner and select "New repository" from the dropdown menu b. click on "Repositories" from the top horizontal menu followed by the big green "New" button![](../resources/rstudio_github1.png)3. fill out the details of the Create a new repository for similar to the following![](../resources/rstudio_github2.png) In particular: - give the repository a name. Typically use the same name as you used for the local repository to avoid confusion - provide a description. Along with the name, this field is searchable so the more detailed it is, the more likely your repository will be discoverable by others as well as yourself in the future - indicate the privacy level. This affects whether your repository is discoverable and readable by anyone (public) or just those you invite (private) - ideally, you also want to include a README file and license in your repository. However, if you enable either of these options in the form, Github will bypass providing a screen with some additional instructions that many find useful for linking your local and remote repository. So on this occasion, we will leave these options as they are4. click the "Create repository" button at the bottom of the page5. Github will present you with the following page:![](../resources/rstudio_github3.png){width=100%} This page presents three alternative sets of instructions that you run locally (on your machine) in order to establish a link between the local and remote repository. You need to run the appropriate set of commands in your local terminal - if no local repository exists, follow the first set of instructions - if you already have a local repository (**as is the case with this demonstration**), follow the second set of instructions - if you intend to import a repository from a different versioning system, follow the last set of instructions6. once you have run the above commands locally, you can refresh the Github page and you will be presented with your remote repository. From here you can navigate through your code, manage privileges etc.If you would like to allow others to collaborate with you on yourrepository, then regardless of whether the repository is public orprivate, you will need to invite them as a collaborator. To do so:1. click on "Settings" from the horizontal menu bar2. click on "Collaborators" from the left sidebar (you may then be asked to submit your password)3. click on the green "Add people" button4. in the popup, enter either the username, full name or email address of the person you want to invite to collaborate with you. Once you click the "Select a collaborator above" and select the appropriate candidate, this person will be sent an invite via email.5. nominate the role that this collaborator can assume (e.g. what capacity does the collaborator have to edit, invite others, alter settings, delete the repository etc)6. repeat steps 3-4 for each additional collaborator you wish to invite# Resolving conflictsIn Git, conflicts arise when changes made in different branches cannotbe automatically merged. This typically happens when two branchesmodify the same part of a file and the changes overlap. Think of itlike two writers revising the same sentence differently. Conflictsusually arise when merging or rebasing branches.When git identifies a conflict, it will mark the conflicting areas(with sets of plain text fences - see below), and it's then up to theuser to resolve them manually by making edits the conflicted file(s)and choosing which changes to keep. After resolving conflicts, thechanges can be staged, and the merge or rebase can be completed.Conflict resolution is an essential skill in collaborative Gitworkflows, ensuring smooth integration of changes from multiplecontributors.Recall that pulling from a remote repository is a to stage process.Firstly the new commits are "fetched" to a new temporary branch andthen this branch is merged into the local repository. Hence, conflictscan occur when pulling from remote repositories. Conflict resolutionin such cases is as outlined above.To illustrate a git conflict, we will start with a very simplerepository, create a branch and then concurrently make edits to thesame part of the same file on each branch before attempting to mergethe branches together. ::: {.callout-note collapse="true"}## Commands to create the repository```{bash}#| label: git_status11#| classes: bash#| engine: bash#| cache: false#| echo: true#| eval: truerm-rf ~/tmp/Repo1mkdir ~/tmp/Repo1cd ~/tmp/Repo1git init echo'File 1'> file1git add file1git commit -m'Initial repo and added file1'echo'---------------'>> file1mkdir dir1echo'* Notes'> dir1/file2git add file1 dir1/file2git commit -m'Modified file1 and added file2 (in dir1)'echo'---'>> dir1/file2echo'temp'> dir1/f.tmpecho'*.tmp'> .gitignoregit add .git commit -m'Modified file2, added .gitignore'git branch Featuregit checkout Featureecho'some text added on Feature branch'>> file1echo'File 3'> dir1/file3git add .git commit -m'New feature'git checkout mainecho' a bug fix on the main branch'>> file1git add .git commit -m'Bug fix in file1'git refloggit log --graph--decorate--oneline--all```:::```{cat}#| label: Fig11a#| echo: true#| cache: false#| class: tikz #| engine.opts:#| file: "../resources/Fig11a.tikz"\documentclass{minimal}\usepackage[paperwidth=35cm,paperheight=10cm,hmargin=0cm,vmargin=0cm]{geometry}%\usepackage[hmargin=0cm,vmargin=0cm]{geometry}\usepackage{fontspec}\usepackage[xelatex,active,tightpage]{preview}\renewcommand{\baselinestretch}{0.75} \usepackage{tikz}\usetikzlibrary{shapes,arrows,shadows,positioning,mindmap,backgrounds,decorations, calc,fit, decorations.pathreplacing,decorations.pathmorphing, shadings,shapes.geometric,patterns} \begin{document}\include{preview}\begin{preview}\input{../resources/common1.tikz}\begin{tikzpicture}\commit{}{commitshash1}{color_inactive}{commitshash1}{commitscomment1}\commit{right = 1.5cm of commitshash1}{commitshash2}{color_inactive}{commitshash2}{commitscomment2}\draw [<-,line width=3pt,draw=black!60] (commitshash1) -- (commitshash2.west);\commit{right = 1.5cm of commitshash2}{commitshash3}{color_inactive}{commitshash3}{commitscomment3}\draw [<-,line width=3pt,draw=black!60] (commitshash2.east) -- (commitshash3.west);% new commit on main\commit{right = 1.5cm of commitshash3}{commitshash5}{color_commit}{commitshash5}{commitscomment5}\draw [<-,line width=3pt,draw=black!60] (commitshash3.east) -- (commitshash5.west);\commit{above = 3cm of commitshash5}{commitshash4}{color_inactive}{commitshash4}{commitscomment4}\draw [<-,line width=3pt,draw=black!60] (commitshash3.north east) -- (commitshash4.south west);%\commit{right = 1.5cm of commitshash4}{commitshash6}{color_inactive}{commitshash6}{commitscomment6}%\draw [<-,line width=3pt,draw=black!60] (commitshash4.east) -- (commitshash6.west);\branch{below = 1cm of commitshash4}{Feature} \master{below = 1cm of commitshash5} \HEAD{below = 0.1cm of master} \end{tikzpicture}\end{preview}\end{document}``````{r, cache=FALSE, echo=FALSE, results='markup'}library(knitr)## commits = system("cd ~/tmp/Repo1; git reflog | awk '{ print $1,\"\\t\",substr($0, index($0,$4)) }'", intern=TRUE)## commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')## commits = commits[rev(1:nrow(commits)),]## colnames(commits) <- c('hash','comment')## commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),15),'..')## lookup = system("cd ~/tmp/Repo1; git log --oneline --decorate main | awk '{ print $1,\"\\t\",substr($0, index($0,$2)) }'", intern=TRUE)## lookup = read.table(textConnection(lookup), header=FALSE, sep='\t')## lookup = lookup %>% mutate(Ref=ifelse(grepl('main',V2),'main',NA))commits = system("cd ~/tmp/Repo1; git log --oneline --all | awk '{ print $1,\"\\t\",substr($0, 9) }'", intern=TRUE)commits = read.table(textConnection(commits), header=FALSE, strip.white=TRUE, sep='\t')commits = commits[rev(1:nrow(commits)),]colnames(commits) <- c('hash','comment')commits$comment <- paste0(strtrim(gsub('.*:','',commits$comment),10),'..') #print(commits)# get the commits from .git/logs/HEADfor (i in 1:dim(commits)[1]) { system(paste0("sed -i 's/commitshash",i,"\\([\\.\\})]\\)/", commits$hash[i], "\\1/g' ../resources/Fig11a.tikz")) system(paste0("sed -i 's/commitscomment",i,"\\([\\.\\})]\\)/", commits$comment[i], "\\1/g' ../resources/Fig11a.tikz")) }system("xelatex -output-directory=../resources ../resources/Fig11a.tikz")system("convert +repage -density 300 -resize 20% ../resources/Fig11a.pdf ../resources/Fig11a.png")```![](../resources/Fig11a.png)::: {.panel-tabset}#### Terminal```{bash}#| label: git-conflict1#| echo: !expr -1#| eval: true#| error: true#| cache: false#| classes: bashcd ~/tmp/Repo1git merge Feature --no-edit```#### RstudioMerging is not directly supported by Rstudio, please use the terminal.:::Hmmm. It appears that there is a conflict (although we should not besupprised since we deliberately set the repository up to have a conflict!). If we explore the a `git diff`, we will see that on the `master` and `Feature` _branches_ have incompatible changes.::: {.callout-note collapse="true"}## More information about the conflict```{bash}#| label: git_branch8a#| echo: !expr -1#| eval: true#| cache: false#| error: true#| message: true#| classes: bash#| engine: bashcd ~/tmp/Repo1git status```The above status informs us that whilst we were able to successfully merge in the changes in `dir1/file3`, the modifications in `file1`remain unmerged (due to conflicts).If we run a `git diff` on the two branches, we will be able to identify all the differences within all comparable files between the two branches.```{bash}#| label: git_branch8b#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git diff main Feature```The output indicates that `dir/file3` does not exist on the `main`branch - this is not a conflict. However, for `file1`, we see thatline three differs between the two branches.It is not so much that they have both made changes to the same file,it is more that the changes are to the same part of the file. Letslook at the contents of `file1` in the commit that is the commonancester of both _branches_:```{bash}#| label: git_branch8c#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git cat-file -p main^:file1```So prior to branching, the `file1` file had two lines of text.In the latest commit on the `main` branch, the `file1` has added a third line with text about a bug fix.```{bash}#| label: git_branch8d#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git cat-file -p main:file1```Yet on the `Feature` branch, the third line of the `file1` filecontains `some text added on Feature branch`.```{bash}#| label: git_branch8e#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git cat-file -p Feature:file1```So the source of this conflict evidently is the third line ofthe `file1` file.:::We can see that the changes made to `file1` are inconsistent. We need to decidewhich edits (if any) we want to use. Recall that the change made in `main` was toaddress a bug or issue. Perhaps this bug or issue does not arise withthe new Feature and thus is superfluous. Alternatively, it might bethat this bug fix is required by both branches (if so, we probablyshould have introduced it to the `Feature` _branch_ at the same timeas the `main` anyway....Lets address the conflict by rolling back `file1` from the `main` _branch_.The following figures depict the `file1` file with conflicts (left) and onceresolved (right).![](../resources/rstudio_gitconflict1.png)![](../resources/rstudio_gitconflict2.png)::: {.panel-tabset}#### TerminalNormally we would resolve this issue using a code editor toedit the actual changes in the file back to the desired condition.However as we only have the one change and this demo is fullyscripted, I will instead roll back (checkout) this one file from theearlier commit on the `main` branch.```{bash}#| label: git_branch8f#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bashcd ~/tmp/Repo1git checkout main^ file1git add .git commit -m'Merge in Feature'```#### Rstudio1. open `file1` for editing2. make the necessary changes to the text3. save the file4. stage (add) the file (you may have to click on the checkbox twice before the tick appears)5. commit the changes with a message like "Merge in Feature" :::::: {.callout-note collapse="true"}## More information about this repository```{r, replace=TRUE}#| label: git-conflict2a#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git reflog``````{r, replace=TRUE}#| label: git-conflict2b#| echo: !expr -1#| eval: true#| cache: false#| classes: bash#| engine: bash1cd ~/tmp/Repo1git log --graph --decorate --oneline --all```:::