{"id":89007,"date":"2020-11-09T14:24:43","date_gmt":"2020-11-09T14:24:43","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=89007"},"modified":"2023-06-21T15:16:36","modified_gmt":"2023-06-21T15:16:36","slug":"git-in-action","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/devops\/tools\/git-in-action\/","title":{"rendered":"Git in action"},"content":{"rendered":"<p><strong>The series so far:<\/strong><\/p>\n<ol>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/devops\/how-we-ended-up-with-git\/\">How we ended up with git<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/devops\/git-anatomy\/\">Git anatomy<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/devops\/git-in-action\/\">Git in action<\/a><\/li>\n<\/ol>\n\n<p>Git is a shell of code hosted in an operating system and capable of processing commands to track and version the content of a tree of directories. Git can work on a single machine under the control of a single user, but it is designed to connect multiple instances running on physically distant machines. More, any repository being tracked locally can be connected to a remote repository. Data can be transferred between local and remote repositories using the set of commands made aptly available.<\/p>\n<p>Git was born to track and version source code files, but by no means it is limited to text files written in some programming language. Although managing the codebase of one software project is the most common use case, nothing stops you from using Git to manage the various stages of a book project or a graphic work. As long as your work creates and updates digital files, Git is here to help.<\/p>\n<p>In this article, you\u2019ll play with the basic Git commands and see how to use them to track and version the constituent files of a sample project. The article focuses on the command-line interface because it\u2019s the quickest way to get a firm grasp of the Git workflow.<\/p>\n<h2>Initializing a Repository<\/h2>\n<p>Once installed on a computer, Git can be used interchangeably through a number of programming interfaces, including Git Bash and Git GUI. On the Windows platform, you can also have two more options to choose: Git CMD and PowerShell. Git Bash is the native command-line interface of Git whereas Git GUI is a visual shell capable of the same basic operations. To launch it, just type git gui in a command window.<\/p>\n<p>When you run Git GUI on a non-monitored directory, it shows the main menu: creating a new repository, cloning, or just opening an existing repository.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"796\" height=\"482\" class=\"wp-image-89008\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/11\/word-image-18.png\" \/><\/p>\n<p>To create a new repository, you need to enter the path to the directory. If you want to do it from the command line, then the command to type in is the following:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">git init<\/pre>\n<p>Here\u2019s how the system would handle it in Git CMD\u2014the dedicated Windows console shell for Git.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1267\" height=\"307\" class=\"wp-image-89009\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/11\/word-image-19.png\" \/><\/p>\n<p>The net effect of creating a new Git repository on a file system directory is the creation of a hidden .git subfolder where several configuration files are stored telling the runtime how to deal with what happens within the boundaries of the subtree. In Windows, to snoop into the hidden <em>.git<\/em> folder, you turn on the <em>Hidden Files<\/em> view in Windows Explorer and head to the folder. Here\u2019s what you see.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1142\" height=\"542\" class=\"wp-image-89010\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/11\/word-image-20.png\" \/><\/p>\n<p>A number of files and subfolders contain all that\u2019s needed to run the Git monitor on the content. In particular, the file named <em>description<\/em> contains the public name of the repository. You can edit its content through a text editor to give it a meaningful description. The <em>HEAD<\/em> file you can see in the figure is also worth some remarks. The contents of the <em>HEAD<\/em> file refer to the current state of the files that you have in the local repository. It\u2019s a pointer to the copy of the files you\u2019re currently working with and the next you\u2019ll save back to the repository.<\/p>\n<p>There are a couple of terms frequently used when explaining the purpose of the HEAD file: one is <em>commit,<\/em> and the other is <em>checkout<\/em>. With the term <em>commit<\/em>, one refers to the state of the stored files at a given time, when the <code>git commit<\/code> command (namely, a save-changes command) was issued. <em>HEAD<\/em> is, therefore, a pointer to the latest version of the files. With the term <code>checkout<\/code>, instead, one refers to the action of switching between different versions of a Git entity such as individual files, entire commits, or branches. When created, a branch is a fork of the current committed state.<\/p>\n<p>In a freshly created and empty repository, the <em>HEAD<\/em> file contains the following:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">ref: refs\/heads\/master<\/pre>\n<p>It reads like the HEAD references the file <em>refs\/heads\/master<\/em>, which is expected to contain the unique identifier (hash) of the most recent commit on the primary (master) branch. In an empty repository, the <em>master<\/em> file in the <em>refs\/heads<\/em> directory doesn\u2019t just exist.<\/p>\n<p>Now learn how it changes when adding some content to the repository.<\/p>\n<h2>Populating a Repository<\/h2>\n<p>Create a text file in the monitored folder, say <em>hello.txt<\/em>, and give it some default content such as the timestamp to easily track it back. If you open <em>Git GUI<\/em> on the sample folder, you should see what\u2019s in the figure.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1836\" height=\"1234\" class=\"wp-image-89011\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/11\/word-image-21.png\" \/><\/p>\n<p>On the left side of the window, you see two docked lists showing <em>unstaged<\/em> and <em>staged<\/em> changes. Unstaged changes refer to files in the folder that are detected as new or modified but have not yet been added to the list of changes you intend to commit at some point in time. The content you commit is any content you may possibly switch back in the future or may use as the starting point for new and independent lines of development (branches). As an author, if you know that the current stage of the <em>hello.txt<\/em> file is destined to change because, in some way transitory or incomplete, you keep it in the unstaged list. On the other hand, if you believe that the file will need to make it to the next commit, then you add it to the stage list. You can keep editing it, but next time you commit the state of the file will be permanently saved and identified with a unique hash.<\/p>\n<p>To move the file to the list of staged changes from the command line, you use the command:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">git add hello.txt<\/pre>\n<p>You can also click Stage changed in the git GUI app.<\/p>\n<p>To remove a file from the list of pending changes to be committed later, you use the command:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">git reset hello.txt<\/pre>\n<p>When issuing the <code>git reset<\/code> command if you don\u2019t specify the name of the file then all pending changes are removed from the list of staged changes. Once you commit pending changes that have been stage, use the <code>git commit<\/code> command to perform the commit. You\u2019ll also have to supply a commit message for each commit<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">git commit hello.txt -m\"Initial commit\"<\/pre>\n<p>The <code>git log<\/code> command will let you inspect the history of the repository.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1278\" height=\"533\" class=\"wp-image-89012\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/11\/word-image-22.png\" \/><\/p>\n<p>Each commit is given a unique identifier that unambiguously references the chunk of changes being made. The log command has many flavors, the most relevant of which as summarized below.<\/p>\n<table>\n<tbody>\n<tr>\n<td>\n<p><strong>git log<\/strong><\/p>\n<\/td>\n<td>\n<p>Displays the entire history of the repository<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p><strong>git log -n <\/strong>N<\/p>\n<\/td>\n<td>\n<p>Displays last N commits<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p><strong>git log &#8211;oneline<\/strong><\/p>\n<\/td>\n<td>\n<p>Displays the entire history of the repository in a compact way, only one line per commit<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p><strong>git log &#8211;stat<\/strong><\/p>\n<\/td>\n<td>\n<p>Displays also which files were altered and the relative number of lines that were added or deleted<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p><strong>git log &#8211;author=<\/strong>pattern<\/p>\n<\/td>\n<td>\n<p>Displays the commits by a given author<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Now see what happens when another change is made to the same <em>hello.txt<\/em> file. The Git GUI application now displays the following. Note that you may have to click Rescan to see the changes.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1083\" height=\"728\" class=\"wp-image-89013\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/11\/word-image-23.png\" \/><\/p>\n<p>The unstaged change is described as the removal of the line below.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"589\" height=\"55\" class=\"wp-image-89014\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/11\/word-image-24.png\" \/><\/p>\n<p>And the addition of the following lines:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"619\" height=\"65\" class=\"wp-image-89015\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/11\/word-image-25.png\" \/><\/p>\n<p>After moving changes to the staging area and placing a commit command you get the following log.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1294\" height=\"843\" class=\"wp-image-89016\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/11\/word-image-26.png\" \/><\/p>\n<p>The log shows the two commits that have occurred, each identified by its unique GUID. The head of the repository points to the master (primary) branch. The last commit is labeled as \u201cChanged made at 01:06PM\u201d which was the text of the commit message. The notes of the latest commit also indicate that one file was changed because some content was inserted.<\/p>\n<p>When it comes to inspecting the content of a repository, you might also want to look into the <code>git status<\/code> command. The command represents the state of the directory and the staging area with pending changes. The output shows which changes have been slated for commit and which have not. It also shows which files in the working folder are not currently tracked by Git. Note that the output of the status command does not include any information about the historical sequence of commits.<\/p>\n<h2>Selecting Files to Track<\/h2>\n<p>By default, all files created in a Git-tracked subtree are subject to the action of the tool. It doesn\u2019t mean, however, that you can\u2019t cherry-pick some files and tell Git to ignore them when performing a commit. A text file named <em>.gitignore<\/em>, placed in the root directory of the repository, instructs the Git engine about files and folders to ignore during a commit. Note that the <em>.gitignore<\/em> file does not affect files already in the repository. Each line of the <em>.gitignore<\/em> file defines a pattern for files to ignore. Usually, the file affects a single repository. However, you can also define global ignore rules, for all repositories on a machine, as below:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">git config --global core.excludesfile ~\/.gitignore_global<\/pre>\n<p>The ignore file itself is treated as a file in the repository, and you might want to commit it if you intend to share the ignore rules with anybody who may happen to clone your repository later.<\/p>\n<p>The content of a <em>.gitignore<\/em> file is critical. Here\u2019s a very basic example of what you can put it in it. In brief, it contains a list of wildcard paths and patterns for locating the files in the subtree to ignore.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\"># Ignore all files whose name matches \"unused\"\r\nunused.*<\/pre>\n<p>Any line that begins with # is treated as a comment and ignored. You can find several predefined and validated examples of ignore files at <a href=\"https:\/\/github.com\/github\/gitignore\">https:\/\/github.com\/github\/gitignore<\/a> and can create a starter ignore file for a given operating system, IDE or programming language visiting the <a href=\"https:\/\/www.toptal.com\/developers\/gitignore\">https:\/\/www.toptal.com\/developers\/gitignore.io<\/a> web site.<\/p>\n<p>As mentioned, all files already checked in at the time the ignore rules are defined are unaffected. In this case, you must untrack them from Git. Here\u2019s the command you need:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">git rm --cached filename<\/pre>\n<p>Ignore rules are not the only way to select which files will be considered for the commit and which not. Another substantially equivalent mechanism passes through the use of the <em>exclude<\/em> file. When a Git repository is created, a text file named <em>exclude<\/em> is automatically created in the <em>.git\/info<\/em> folder. Its content is equivalent to <em>.gitignore<\/em> and follows the same syntax rules. The difference between ignore and exclude rules is that ignore rules are devised to be shared among project members having access to the repository, whether remote or cloned. Exclude rules remain local to the repository and are meant to be mostly personal rules.<\/p>\n<h2>Going to a Specific Revision<\/h2>\n<p>The crucial benefit of Git is that it keeps track for you of multiple revisions of the same files. From the command line, the <code>git log<\/code> command lets you see the list of revisions, each identified by a hash code. How would you access a specific revision of the project that was created a while back? You use the <code>checkout<\/code> command with the hash code of the commit.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">git checkout hashcode<\/pre>\n<p>When running the git log command, you see the hash code <em>7eb9b60\u2026<\/em> referencing the first sample commit where the <em>hello.txt<\/em> file contained only one line of text.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1294\" height=\"843\" class=\"wp-image-89017\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/11\/word-image-27.png\" \/><\/p>\n<p>Here\u2019s the command to switch the repository back to on older state and gain access to the files in the repository at the time of the given commit.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">git checkout 7eb9b60e6b8a6c12535a24b878d4dc6e4091270b<\/pre>\n<p>Here\u2019s the output.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1597\" height=\"755\" class=\"wp-image-89018\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/11\/word-image-28.png\" \/><\/p>\n<p>The Windows folder now shows a <em>hello.txt<\/em> file and no other tracked file like <em>.gitignore<\/em>. In the figure below, you see the unused.txt file because it\u2019s marked to be ignored by Git. The content of the <em>hello.txt<\/em> is the expected old one.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1209\" height=\"783\" class=\"wp-image-89019\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/11\/word-image-29.png\" \/><\/p>\n<p>When Git receives the checkout command, it then places all files in the specified revision in the working folder. In this case, it just replaced the latest hello.txt with an older version of it. What you do next depends on the reason that led you to check out an older version of the project. If you want to save a copy of the files, all you do is copy the file to a different folder and go. The new folder is disconnected from Git (or it could even be a new distinct Git repository) and can be managed as appropriate.<\/p>\n<p>However, when you checkout an older commit the state of repository changes, as the output of the command shows.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">git checkout 9605a5c08<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"548\" height=\"80\" class=\"wp-image-89020\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/11\/word-image-30.png\" \/><\/p>\n<p>After the checkout of a commit, the repository is in a detached <em>HEAD<\/em> state. The detached <em>HEAD <\/em>state is legitimate but potentially dangerous. The reason is that when you checkout a specific commit from a repository, the <em>HEAD<\/em> pointer\u2014namely, the reference to the current working revision in the repository, is not updated. This means that the files in the working area of the repository and the pointer are no longer in sync. Is this a problem? Well, the moment you edit any of the checked-out files and force to commit changes back, these changes won\u2019t belong to any revision and will likely be lost if you later check out another revision. In other words, commits out of a detached <em>HEAD<\/em> state are not tracked and can hardly be retrieved later. The only way to check them outat a later time is by remembering the exact hash code\u2014a GUID\u2014of the latest commit. Here\u2019s an example:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">git checkout 4505ddc08<\/pre>\n<p>To revert to the master repository that you left when checked out an earlier commit, use the command below. Note though that the command switches back the <em>HEAD<\/em> pointer to master and loses all of the intermediate changes on the older commit.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">git checkout master<\/pre>\n<p>To avoid the detached <em>HEAD<\/em> issue entirely, you should use another key concept of Git\u2014the branch. The branch is an independent and fully supported line of development of the project. It has a name and can be created upon checkout. Branching is a crucial topic, though, and deserves its own space to be appropriately described. I\u2019ll get into that in the next article.<\/p>\n<h2>Summary<\/h2>\n<p>In the end, the Git workflow is straightforward. You initialize a repository on an existing folder, you work on it creating and editing files, track files you\u2019re interested, and commit changes. Any committed change represents the snapshot of the project you want to preserve, or return to, later. To switch back to a previous version and to inspect the status of the repository, you have other dedicated commands. This is the essence of Git and to perform all these actions, you can count on a number of visual and command-line tools. In the next article of this series, I\u2019ll focus on a few more specific and advanced Git commands such as branch, stash and merge.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dino Esposito continues his series on git. In this article, he explains how to initialize a repository, track files, and revert to a specific revision.&hellip;<\/p>\n","protected":false},"author":221911,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143521],"tags":[124952],"coauthors":[6780],"class_list":["post-89007","post","type-post","status-publish","format-standard","hentry","category-tools","tag-redgate-deploy"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/89007","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221911"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=89007"}],"version-history":[{"count":9,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/89007\/revisions"}],"predecessor-version":[{"id":97192,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/89007\/revisions\/97192"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=89007"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=89007"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=89007"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=89007"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}