Skip to content

Things I Keep Forgetting

Estimated time to read: 8 minutes

  • Last Updated: May, 2024

Different ways to use git add

Sometimes I make changes to many files but don't want to individually stage them. I found this great Stackoverflow thread highlighting the differences between the various git add commands. I mostly use git add -u to stage modified and deleted files only.

Reference: https://stackoverflow.com/questions/572549/difference-between-git-add-a-and-git-add

What's in the .git folder?

I found this video provides a great explanation of the hidden .git folder

The definitive deep dive into the .git folder - Rob Richardson - NDC Porto 2022

https://www.youtube.com/watch?v=YWX-taj2iH0

Git Branching

This is a great visual resource for learning how Git branching works.

https://learngitbranching.js.org/

Track an empty folder using Gitkeep

I was recently building a workshop and had all the code working on my laptop. I pushed it to my Github repo but later when trying to run the lab on a different machine, there was an error that a folder was missing.

One of the folders I used was named output and was used to store files which were created when the script ran. Therefore the folder was empty on my machine.

When I ran git add and git commit I didn't realise that the empty output folder was not part of the committed objects.

This is because Git only tracks and permits files, not directories. Directories are added as a side effect when adding files inside them.

https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/Git_FAQ.html#Can_I_add_empty_directories.3F

To resolve this you can add an empty file with any name to the folder you want to push to your repository. The common convention is to call the file .gitkeep so others know the purpose. The . at the start of the file name makes this file hidden.

Git Revert and Git Reset

Here is the behaviour of the git revert and the git reset commands

To setup the example, three commits were created with each commit adding a new line of text to a file.




Git Revert

git revert HEAD undoes a commit by creating a new commit that reverses the changes made in the original commit. HEAD is a reference to the current commit, representing the snapshot of the project at a given point in time.

As you can see in the screenshots below, after reverting the commit, the original three commits are still maintained however a new fourth commit has been created which reverts the third commit. i.e. it deletes the line that was previously added.

Warning

git revert will revert all changes in a specified commit. In these examples there is only one change being made so be careful if you have more than one change.

Git Reset

git reset allows you to move the HEAD and current branch to a specific commit.

Warning

Using --hard will discard any uncommitted changes and permanently remove them. You can leave out --hard if you want to still have the changes in your files.

In this example the HEAD has moved back to the original commit. Note the git log output to show the four commits, after the git reset only one commit remains which was the original.

Orphaned Commits and Reference Logs

You might be wondering what happened to the other commits. They're not deleted, just orphaned.

In Git, an orphaned commit refers to a commit that is not reachable by any branch or tag in the repository's commit history. This means that the commit is no longer part of the main branch or any other branch in the repository.

Orphaned commits can occur in a few scenarios, including:

  • Forcefully deleting a branch: If a branch is deleted using the "-D" option in Git, the commits that were only reachable through that branch become orphaned.
  • Resetting or rebasing: Performing a hard reset or using interactive rebase can also result in orphaned commits if they are no longer reachable by any branch after the operation.

Orphaned commits are not automatically removed from the repository since Git retains all commits by default to allow for a comprehensive commit history.

Running git log --graph --reflog will display the graphical commit history along with the reflog.

The "--graph" option visualizes the commit history as a graph, showing the relationships between different branches and commits. This can help in understanding the branching and merging of code in a repository.

The "--reflog" option includes the reference logs in the output. Reference logs are used to keep track of changes made to branch pointers, tags, and other references in the repository. Including the reflog in the output can provide additional information about the changes and movements of these references.

In this simple example git reset was used to set HEAD to the initial commit. You can see the later orphaned commits above the HEAD -> main. The HEAD was then moved back to the latest commit 15326f82c908b74e5fee59bd9d472cf16f0fc465

If you need to recover from orphaned commits the example above may not be realistic. More likely you would have orphaned commits and subsequent commits, ending up similar to this diagram.

In this scenario you can't reset to the orphaned commit (15326f) because you lose out on the new commits (e2de62). You also can't remain on the current HEAD (e2de62) because you want to recover what was in the ophaned commits (15326f)

In that case you may be able to create and checkout a new branch with the orphaned commit hash, git checkout -b <branch-name> <commit-orphaned-hash>. You can then merge back into the main branch, git merge main, with the non-orphaned commits

Another Git Revert Example

The previous example demonstrated reverting the current commit. Sometimes you may have added/updated/deleted some configuration in an earlier commit that you now need to revert. This is also possible by referencing the commit ID. So instead of git revert HEAD you would use git revert <commit id>. You can find the commit ID by using the git log command.

In this example the original commit contains four lines of text. The second line is removed in the second commit and then additional changes are made in the subsequent commits.

Later on the second line of text is needed again and so the configuration is reverted from the second commit where this line was removed. Note that the other lines of text don't change.

Here is the original commit with four lines of text

The commit log after removing a line of text and subsequent commits

The file with the second line removed and the fifth line of text added

Running the git revert 92bf81c7378a1e99d1ab2cfa53117509a7450871 command to revert the second commit

The commit log showing a new commit with the reverted text

The file after the revert. Note that the second line has been added back but no other changes have been made.

Comments