Authors Allex Oliveira Guillermo Polito Stéphane Ducasse
License CC-BY-SA-3.0
Manage Your Code with Git and Iceberg Guillermo Polito and Stéphane Ducasse with Allex Oliveira May 12, 2020 Copyright 2017 by Guillermo Polito and Stéphane Ducasse with Allex Oliveira. The contents of this book are protected under the Creative Commons Attribution- ShareAlike 3.0 Unported license. You are free: • to Share: to copy, distribute and transmit the work, • to Remix: to adapt the work, Under the following conditions: Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work). Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same, similar or a compatible license. For any reuse or distribution, you must make clear to others the license terms of this work. The best way to do this is with a link to this web page: http://creativecommons.org/licenses/by-sa/3.0/ Any of the above conditions can be waived if you get permission from the copyright holder. Nothing in this license impairs or restricts the author’s moral rights. Your fair dealing and other rights are in no way affected by the above. This is a human- readable summary of the Legal Code (the full license): http://creativecommons.org/licenses/by-sa/3.0/legalcode Layout and typography based on the sbabook LATEX class by Damien Pollet. Contents Illustrations iv 1 Preamble 1 2 Getting Started with Git 3 2.1 Creating a Repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.2 git clone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.3 Making Changes: How does git track my Changes? . . . . . . . . . . . . . 6 2.4 Commiting your Changes . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.5 Synchronizing with your Remote Repository . . . . . . . . . . . . . . . . . 11 2.6 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.7 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 3 Understanding Git 17 3.1 Some git Internals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.2 Understanding Detached HEAD . . . . . . . . . . . . . . . . . . . . . . . . 21 3.3 Merging history lines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3.4 Commit in workflow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.5 Creating new history lines with branches . . . . . . . . . . . . . . . . . . . 26 3.6 Interacting with Remote Repositories . . . . . . . . . . . . . . . . . . . . . 29 3.7 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 4 Practical Git Scenarios 37 4.1 Before commit little helpers . . . . . . . . . . . . . . . . . . . . . . . . . . 37 4.2 Exploring the History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 4.3 Discarding your Local Committed Changes . . . . . . . . . . . . . . . . . . 40 4.4 Ignoring Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 4.5 Commiting a File Filtered out by the .gitignore . . . . . . . . . . . . . . . . 41 4.6 Getting out of Detached HEAD . . . . . . . . . . . . . . . . . . . . . . . . 42 4.7 Accessing your Repository through SSH . . . . . . . . . . . . . . . . . . . . 42 4.8 Rewriting the History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 4.9 How to Overwrite/Modify Commits . . . . . . . . . . . . . . . . . . . . . . 44 4.10 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 i Contents 5 Publishing your first Pharo project with Iceberg 47 5.1 For the impatient . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 5.2 Basic Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 5.3 Create a new project on Github . . . . . . . . . . . . . . . . . . . . . . . . 48 5.4 [Optional] SSH setup: Tell Iceberg to use your keys . . . . . . . . . . . . . . 48 5.5 Iceberg Repositories browser . . . . . . . . . . . . . . . . . . . . . . . . . 50 5.6 Add a new project to Iceberg . . . . . . . . . . . . . . . . . . . . . . . . . 50 5.7 Repair to the rescue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 5.8 Create project metadata . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 5.9 Add and commit your package using the Working copy browser . . . . . . . 54 5.10 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 6 Configure your project nicely 59 6.1 What if I did not create a remote repository . . . . . . . . . . . . . . . . . . 60 6.2 Defining a BaselineOf . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 6.3 Loading from an existing repository . . . . . . . . . . . . . . . . . . . . . . 63 6.4 [Optional] Add a nice .gitignore file . . . . . . . . . . . . . . . . . . . . . . 63 6.5 Going further: Understanding the architecture . . . . . . . . . . . . . . . . 64 6.6 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 7 Empowering your projects 67 7.1 Adding Travis integration . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 7.2 On windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 7.3 Adding badges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 7.4 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 8 Contributing to Pharo 71 8.1 In a nutshell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 8.2 Step 0: Setting up the development environment . . . . . . . . . . . . . . . 71 8.3 Fork the Pharo repository . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 8.4 Setup Iceberg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 8.5 Step 1. Setting up your repository . . . . . . . . . . . . . . . . . . . . . . . 73 8.6 Step 2: Work on your image and push your change . . . . . . . . . . . . . . 75 8.7 Step 3: Follow your pull request . . . . . . . . . . . . . . . . . . . . . . . . 76 8.8 Step 4: Once your pull request is integrated . . . . . . . . . . . . . . . . . 76 8.9 Why you do not need to resync your fork with the pharo repo? . . . . . . . 77 8.10 Update your Pharo fork using the command line . . . . . . . . . . . . . . . 77 8.11 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 9 Tips and Tricks 79 9.1 How to use SSH keys . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 9.2 How to contribute back to a project . . . . . . . . . . . . . . . . . . . . . . 80 9.3 How to distribute your changes in different branches . . . . . . . . . . . . 80 10 Iceberg Glossary 83 10.1 Git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 10.2 Iceberg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 ii Contents Bibliography 87 iii Illustrations 2-1 A Repository as a timeline of changes. . . . . . . . . . . . . . . . . . . . . 3 2-2 Creating a New Repository on Github. . . . . . . . . . . . . . . . . . . . . 4 2-3 A Repository Page for a project called test in GitHub. . . . . . . . . . . . . . 5 2-4 Basic git architecture: You change the files in your working copy, commit changes to local repository and synchronize your local repository with remote ones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2-5 Getting the HTTPS url of your repository from GitHub. . . . . . . . . . . . . 6 2-6 Push is an operation that sends commits from your local repository to a remote repository. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2-7 Overview of git basic operations: add, commit, pull and push (+ extra fetch and merge). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 3-1 git repository structure: the working copy, the repository, and the remote repositories. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3-2 Graph of commits. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3-3 git references: a reference refers to a commit. HEAD to a branch and tags are special fixed references. . . . . . . . . . . . . . . . . . . . . . . . 20 3-4 Detached HEAD after checking out a tag: HEAD refers to a commit and not a branch anymore. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3-5 Merging the history with a merge commit. . . . . . . . . . . . . . . . . . . 23 3-6 Commit is an operation that stores things from your working copy into your local repository. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3-7 History graph after our first commit. . . . . . . . . . . . . . . . . . . . . . 26 3-8 History graph after our second commit . . . . . . . . . . . . . . . . . . . . 26 3-9 History lines can be branched from a commit. . . . . . . . . . . . . . . . . 27 3-10 A new branch points by default to the same commit as the current branch. . 27 3-11 Divergent history. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 3-12 Fetch is an operation that brings things from a remote into your local repository. Merge will join the remote history with your current history and update your working copy. Pull will do both of them. . . . . . . . . . . 32 3-13 Push is an operation that sends commits from your local repository to a remote repository. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 4-1 Example of SourceTree’s commit graph view. . . . . . . . . . . . . . . . . . 39 4-2 Example of Github’s commit graph view. . . . . . . . . . . . . . . . . . . . 40 iv Illustrations 5-1 A distributed versioning system. . . . . . . . . . . . . . . . . . . . . . . . 48 5-2 Create a new project on Github. . . . . . . . . . . . . . . . . . . . . . . . . 49 5-3 Use Custom SSH keys settings. . . . . . . . . . . . . . . . . . . . . . . . . 49 5-4 Iceberg Repositories browser on a fresh image indicates that if you want to version modifications to Pharo itself you will have to tell Iceberg where the Pharo clone is located. But you do not care. . . . . . . . . . . . . . . . 50 5-5 Cloning a project hosted on Github via SSH. . . . . . . . . . . . . . . . . . 51 5-6 Cloning a project hosted on Github via HTTPS. . . . . . . . . . . . . . . . . 51 5-7 Just after cloning an empty project, Iceberg reports that the project is missing information. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 5-8 Adding a project with some contents shows that the project is not loaded - not that it is not found. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 5-9 Create project metadata action and explanation. . . . . . . . . . . . . . . . 53 5-10 Showing where the metadata will be saved and the format encodings. . . . 53 5-11 Adding a src repository for code storage. . . . . . . . . . . . . . . . . . . . 54 5-12 Resulting situation with a src folder. . . . . . . . . . . . . . . . . . . . . . . 54 5-13 Details of metadata commit. . . . . . . . . . . . . . . . . . . . . . . . . . 55 5-14 Adding a package to your project using the Working copy browser. . . . . . . 55 5-15 Iceberg indicates that your package has unsaved changes – indeed you just added your package. . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 5-16 When you commit changes, Iceberg shows you the code about to be committed and you can chose the code entities that will effectively be saved. 56 5-17 Once changes committed, Iceberg reflects that your project is in sync with the code in your local repository. . . . . . . . . . . . . . . . . . . . . 56 5-18 Publishing your committed changes. . . . . . . . . . . . . . . . . . . . . . 57 6-1 Creating a local repository without pre-existing remote repository. . . . . . 59 6-2 Opening the repository browser let you add and browse branches as well as remote repositories. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 6-3 Adding a remote using the Repository browser of your project (SSH version). 61 6-4 Adding a remote using the Repository browser of your project (HTTP version). 61 6-5 Once you pushed you changes to the remote repository. . . . . . . . . . . . 61 6-6 Added the baseline package to your project using the Working copy browser. 62 6-7 Architecture. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 8-1 A fresh Pharo image by default indicates that it does not find the clone of Pharo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 8-2 Repairing the Pharo project. . . . . . . . . . . . . . . . . . . . . . . . . . . 74 8-3 Solving the detached working copy situation. . . . . . . . . . . . . . . . . . 75 8-4 Checking your pull request. . . . . . . . . . . . . . . . . . . . . . . . . . . 76 8-5 Command Line. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 9-1 Checkout choices. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 v CHAPTER 1 Preamble Git is the defacto standard distributed source versioning system. Even though Pharo got its own distributed versioning system during more than 12 years. It lacked the maturity of git and one key features: branches. This is why since Pharo 6.0 it was clear that Pharo needed to have support for git. Pharo started to support git since Pharo 6.0. Pharo 7.0 saw a major version since all its code and development migrated to git. However managing Pharo with git is not just a matter of saving code into files and versioning. Any fool can do that in one afternoon. Pharo has a pow- erful reflective layer and execution change the objects that represent code itself. Therefore Pharo as a living system can be in a different state than the file checkouted from a git repository. From this we can imagine many com- plex scenarios that even smart programmers would have headaches to un- derstand. Therefore the Pharo consortium (E. Lorenzano, N. Passerini, G. Polito and P. Tesone) developed an advanced tool to help managing the live programming aspect of Pharo and the static perspective that file-based versioning systems such git have of the reality. Iceberg not only supports the management of large projects (such as Pharo itself with more than 600 packages and a couple of thousand classes) but it helps us (the developers) to understand the situa- tion between our image, the files on our disc and the multiple branches and remote repositories that the git model offers. It offers strategies to address problems we may face. Iceberg is a tool to manage git projects from Pharo. It makes the experience of managing code really smooth. A major effort went into the version of Ice- berg present in Pharo 7.0. We are using the version of Iceberg available in Pharo 8.0. 1 Preamble This document is under writing but we decided to release before its comple- tion because managing code can be a large topic when we start to discuss workflow and process. The book is structured for now in two main parts 1. Understanding from the command-line 2. Managing Pharo code with Iceberg. The authors want to thank Sean de Nigris, Quentin Ducasse and Stefan Egger- mont for the reviews and copy-edit of the early version. We also thank Peter Uhnak for his first blog on publishing Pharo code on Github. We thank Iona Thomas for the enhancements of Iceberg. 2 CHAPTER 2 Getting Started with Git In this chapter we introduce the basics of git and VCSs through guided ex- amples. We first start by setting up a repository in a remote server and then load it in our own machine. We then show how we can inspect the state of our repository and save our files into it. Once our changes are saved, we show how we can push our changes to our remote repository in a distant server. This chapter will assume you have git already installed in your machine, and that you’re using a *nix operating system. Moreover, you will see that we will approach git with the command-line. Don’t be affraid if you’ve never used it before, it is not as difficult as it may seem and you will get used to it. There is always a first time! Also, we promise you that everything you learn in here can be applied to, and will actually help you better understand, non command-line tools. on le tor y file ers i afi ec ... ed tv de d dir difi firs ad w mo ne t1 t2 t3 t4 t5 Figure 2-1 A Repository as a timeline of changes. 3 Getting Started with Git Figure 2-2 Creating a New Repository on Github. 2.1 Creating a Repository A git repository is a store of files and directories. The big difference be- tween a git repository and a simple filesystem is that the changes we make are stored as events, like a timeline (Figure 2-1). Git not only stores such a timeline but also allows us to query it, undo some of its changes, and so on. Before working with such a history/repository we need to set it up. Fortu- nately, setting up a git repository is much lightweight than setting up a database repository. Though there are many different ways to create a git repository, we will start with a simple solution to be up and running as fast as possible. We will study other ways to setup repositories in Chapter 3. Let’s proceed to create a repository in an online hosting service such as GitHub or GitLab. On GitHub use the New Repository action. Figure 2-2 shows the kind of form GitHub provides to its users to create a new repository. Following the form/wizard will eventually get you a running repository on- line. You will be most probably then redirected to your repository page. Fig- ure 2-3 shows how such a page looks in GitHub. We are almost set to work from the command line now. However, we need to set-up our mind around a couple of extra concepts. The repository we just created does not exist in our machine. Actually, it is stored in some server maintained by github/gitlab. To interact with this repository we will need a network connection. We will call this repository, living in a remote server, a remote repository. 4 2.2 git clone Figure 2-3 A Repository Page for a project called test in GitHub. Your PC github.com Commit remote local remote repository repository Working Copy Figure 2-4 Basic git architecture: You change the files in your working copy, com- mit changes to local repository and synchronize your local repository with remote ones. 2.2 git clone Git, constrastingly to other VCSs, is a distributed VCS. This has a lot of con- sequences in the way we work, that we will study in detail in Chapter 3. For now, you will have to remember only one thing: instead of being connected all the time to our remote repository, we will work on the repository on your machine (called a local repository). Eventually, we will synchronize the state between our local repository with the remote one (Figure 2-4). This is what makes possible the disconnected or off-line workflow that people often praise in git. Making a local copy of a remote repository is such a common task that git has a dedicated command for it, the git clone [url] command. The git clone command receives as argument the URL of our repository, that we can get from our repository page. You will see that your repository page will offer you different URL options, the most used being SSH and HTTPS urls. We will use in this chapter HTTPS URLs because they have an easier setup, but for those readers that are curious, Section ?? compares SSH and HTTPS, and Section 4.7 shows how to setup your SSH environment. To obtain the HTTPS URL of your repository, go to your repository’s page 5 Getting Started with Git Figure 2-5 Getting the HTTPS url of your repository from GitHub. and look for it under the clone/HTTPS options. As an example, Figure 2-5 illustrates how to get such url from a GitHub project page. Copy that url and use type your command as in: $ git clone [url] Cloning into '[your_project_name]'... remote: Counting objects: 11082, done. remote: Compressing objects: 100% (4/4), done. remote: Total 11082 (delta 2), reused 6 (delta 2), pack-reused 11076 Receiving objects: 100% (11082/11082), 4.35 MiB | 1.22 MiB/s, done. Resolving deltas: 100% (4063/4063), done. Checking connectivity... done. When the command finishes, git is done creating a directory named as your repository (your_repo_name). We will call this directory the working direc- tory as it is where we will work and interact with our repository. The work- ing directory, with all the files and directories it contains, is managed by git and linked to our repository. Do not worry, there is nothing else you need to do to keep this link, git will automatically track your changes for you. You are now ready to go and start working on your project. 2.3 Making Changes: How does git track my Changes? Let’s now dive in our working directory and start making changes to it. We’ll for example create a file called project.txt, then open it with a text editor and add some lines to it. $ cd your_project_name $ touch project.txt ... After some time working, we can use the ls command to check the files in my directory. $ ls project.txt README.md 6 2.3 Making Changes: How does git track my Changes? And then the cat command to check their contents from the command line. $ cat project.txt # Done - created the repository - ==git== clone - created this file # To do - commit this file - push it to my remote repository $ cat README.md #My Project This project is an example ==git== repository used to learn ==git==. Check the project.txt file for information about pending tasks. Basically, we have modified some files, but we have done no git at all. What does git know about these files at this point? git does Nothing without your Permission A new important thing to grasp about git at this point is that it will do noth- ing until we explicitly ask it to do it. In this sense, git is not any kind of repository but a transactional repository. All changes we do in our working directory are not stored by git automatically. Instead, we need to explicitly store them using a commit command, as we do with transactional database to store any data. The transactional aspect means also that: • while we do not commit, we can easily rollback our changes; • the other side of the coin, until we commit all our changes are in a transient state, and we may lose them. We will see more of this explicitness applied in other cases in the course of this book. Sometimes it may seem that git is just dumb, and that it cannot guess what we want to do when it is obvious. However, the case is that git has so many possibilities that not guessing is the healthier decision in most cases. Especially when considering destructive operations that may make you lose hours of work. git status We can then turn our question around: Does git know something about these files? Git indeed tracks our files to know what should be saved and what should not. We can use this information to see what are actually the 7 Getting Started with Git changes that happened while we were working. The git status command produces a list of the current changes. $ git status On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: README.md Untracked files: (use "git add <file>..." to include in what will be committed) project.txt no changes added to commit (use "git add" and/or "git commit -a") Reading the output of git status we can see that it lists several informa- tion: • Changes not staged for commit. Lists the modified files that git al- ready knows. • Untracked Files. Lists the files that are in the working directory but were never added under the control of git. Also, git status shows some hints about possible commands that we may use next such as git add or git checkout. git and directories You may have observed a curious behaviour with directories. If you create an empty directory and then use the git status command, you’ll notice the directory is not listed at all. It even looks like the directory is being com- pletely ignored. For example, if we try adding an empty directory into a new repository git will actually say working tree clean as if there were no changes at all. $ mkdir emptyDirectory $ git status On branch master nothing to commit, working tree clean Indeed, git does not manage empty directories but only files. Directories are only modelled as paths to get to files. No extra information is tracked for them. In other words, we cannot just store directories into git. And in case we want to do it for some reason, we need to put files into them. 8 2.4 Commiting your Changes 2.4 Commiting your Changes We would like now to save our changes in our git repository. This way, if anything happens, we can always recover our work up to this point. We have said before that the operation of saving our work in the repository is called a commit. If we try the git commit command we will see this is not as direct as expected. $git commit On branch master Initial commit Untracked files: README.md project.txt nothing added to commit but untracked files present If we read Git’s message, we will notice that though it has correctly identified that we have new files, git is asking us to add them before it can commit them. The Groceries Metaphore To make it simple, you can see this whole tracking story as going to the su- permarket. Imagine you make your grocery list and go to the supermarket. To get our groceries and take them home, we need first to go look for them, put them in our shopping cart, and then go and pay for them. An extra ser- vice may propose to take your grocery list and do the groceries for you. But such an extra service requires that you make a list up-front. Same apply with git, the default behavior of git is not to commit all the changes. Partly because Git’s philosophy is to ask the user explicitly what to do, which in this case is translated to asking what to commit. Instead, git requires us to add the files we want to commit to a list of added files, also called in git terminology the staging area, and equivalent to your shopping list. Once our staging area is full with the changes we want to commit, we can commit such changes using the git commit command. A First Commit Adding changes to your staging area is done through the git add [file] command. Let’s proceed to add our changes and see what is the status of our repository afterwards. 9 Getting Started with Git $ git add README.md $ git add project.txt $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: README.md new file: project.txt Now git says that our two new files are listed as ”to be committed”. Let’s now proceed to save our changes in the repository with the git commit -m "[message]" command. The message used as argument of this command is a piece of text that we can use to explain the contents of the changes, or the intention of our changes. $ git commit -m "first version" [master a93c016] first version 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 project.txt If we check the status of our repository after the commit is done, we see that it has changed. There is nothing to commit: $ git status On branch master nothing to commit, working directory clean Add then Commit, all over again If we repeat the process and we change one of our existing files, we will see something interesting. Commiting our changes in a file we added before re- quires that we do a git add [file] and git commit again on the same file, even if git already knew about it. $ cat project.txt # Done - created the repository - ==git== clone - created this file - commit this file # To do - push it to my remote repository $ git status On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) 10 2.5 Synchronizing with your Remote Repository modified: project.txt no changes added to commit (use "git add" and/or "git commit -a") This is because even if on the surface git seems to manage files, it actu- ally manages changes to those files. Technically speaking, the changes we have done are new changes, so we have to tell git we are interested in those changes. $ git add project.txt $ git commit -m "Commit is not in ToDo anymore" [master e14a09f] Commit is not in ToDo anymore 1 file changed, 1 insertion(+), 1 deletion(-) 2.5 Synchronizing with your Remote Repository So far we have worked only on the local repository residing in our machine. This means that mostly all of git features are available without requiring any internet connection, making it suitable for working off-line (think on working on the train or with a constrained connection!). However, working off-line is a two-edged sword: all your changes are also captive in your ma- chine. While your changes are in your machine, nobody else can contribute or collaborate to them. Moreover, losing your machine would mean losing all your changes too. Keeping your changes safe means to synchronize them from time to time with your remote repository. git’s metaphore for remote synchronization is based on the ideas of pulling and pushing changes between repositories. git takes the perspective that we are located in our local repository. We bring other’s changes by pulling them from remote repositories to our local repository. We send our changes by pushing them from our local repositories to one or many remote repositories. Getting Remote Changes with git pull Before being able to share our commits in some external server, we need be- fore to update our repository to avoid them being de-synchronized. While you can always try to share your commits by directly pushing (see Section 3.6), you will see with experience that git favors pulling before pushing. This is, among others, because in your local repository you have complete control to do whatever manipulation you want, what is especially important to solve mistakes and merge conflicts. You cannot do the same in your re- mote repository. In our example pulling does not seem really necessary because you are the only person modifying your repository. No new changes happened in the remote repository in the meantime. However, let’s imagine that you have 11 Getting Started with Git done a modification in this same repository from another machine or even a different clone in the same machine (which are totally feasible scenarii). In that case, you would like to update your local repository with those new changes. Updating our repository is done through the git pull command. Pulling will update our database and then update our files. $ git pull remote: Counting objects: 2, done. remote: Compressing objects: 100% (1/1), done. remote: Total 2 (delta 1), reused 2 (delta 1), pack-reused 0 Unpacking objects: 100% (2/2), done. From https://github.com/guillep/test 1656797..a2dbd8b master -> origin/master Updating 1656797..a2dbd8b Fast-forward newfile | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 newfile A Bit on Merging We will see in detail in Section 3.6 that git pull performs two different op- erations: a fetch and a merge. The fetch lookups new commits in remote repositories. The merge, studied in detail in Section 3.3, takes the state in the remote repository and your local repository and tries to make a single version of of that. Three different scenarii can actually happen from a pull operation, which will be: • Fast-forward: the updates were applied without needing a merge. • Automatic Merge: the updates were applied without conflicts. git had to do a merge commit and will ask you for a commit message. • Merge Conflict: The changes you did and incoming changes affect some common files. In this case git does not know what version to keep (or even if a mixture is possible) and asks you to solve it manually before doing a new commit. Once the merge is resolved, your working copy is updated with the new ver- sion of your repository. Luckily for us, fast-forward and automatic merges are the simplest and more common ones. They require almost no manual interaction other than introducing a message. Sending your Changes with git push The final step in our git journey is to share our changes to the world. Such sharing is done by pushing commits to a remote repository, as shown in Fig- ure 3-13. To push, you need to use the git command git push [remote] 12 2.5 Synchronizing with your Remote Repository Your PC push github.com commit local remote Working Copy repository remote repository Figure 2-6 Push is an operation that sends commits from your local repository to a remote repository. [remote_branch]. This command will send the commits pointed from your your current branch to the remote [remote] in the branch [remote_branch]. $ git push origin master Counting objects: 3, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 271 bytes | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@github.com:[your_username]/[your_repo_name].git b6dcc3f..f269295 master -> temp A Branch’s Upstream We can omit the destination branch and remote from the command, relying on git default values. By default a git push operation will push to the so called branch’s upstream. A branch’s upstream is a configuration specifying a pair (remote, branch) where we should push by default that branch. When we clone a repository, the default branch comes with an already configured upstream. We can interrogate git for the branch’s upstream with the super verbose flag in the branch command, i.e., git branch -vv, where we can see for example that our master branch’s upstream is origin/master, while our development branch has no upstream. $ git branch -vv # doubly verbose! development 1656797 This commit adds a new feature master f269295 [origin/master] First commit When a branch has no upstream, a push operation will by default fail with a git error. Git will ask us to set an upstream, or otherwise specify explicitly a pair remote/branch for each push. 13 Getting Started with Git $ git push fatal: The current branch test has no upstream branch. To push the current branch and set the remote as upstream, use ==git== push --set-upstream origin test $ git push --set-upstream origin test Counting objects: 3, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 271 bytes | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@github.com:[your_username]/[your_repo_name].git b6dcc3f..f269295 master -> test Pushes can get Rejected In some scenarii git may reject our pushes, so they are not saved to the re- mote repository. In general git rejects changes when the remote reposi- tory has diverged from ours. Of course a rejection may also happen when we don’t have write permissions in the remote repository. The typical error shows something like the following: $ git push To git@github.com:guillep/test.git ! [rejected] master -> master (fetch first) error: failed to push some refs to 'git@github.com:[your_username]/[your_repo_name].git' hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first integrate the remote changes hint: (e.g., 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details. As the error message says, the remote has changes that we do not have lo- cally. In other words, our push has been rejected because otherwise we would have overwritten the remote changes. Instead, we need to take the remote changes and mix and match them with our changes, by applying a pull (Sec- tion ??) and a merge (3.3). After the pull, our repository will have our out- going changes, but no more incoming changes, and so our push will not be rejected 14 2.6 Overview Your PC pull github.com fetch merge remote repository Working Copy local repository push add + commit Figure 2-7 Overview of git basic operations: add, commit, pull and push (+ extra fetch and merge). 2.6 Overview In this chapter we have studied the basic operations of git. We have seen that a new repository starts in your local machine with a git clone that creates a working copy directory. Changes in our working copy are tracked by git automatically and we can query the tracking using git status. We can then proceed to operate on our changes as illustrated in 2-7. We have seen that: • git add tells git about files to track for commit. • git commit finishes a transaction and stores our changes in a commit. • git pull synchronizes a remote repository with our local repository by doing first a fetch and then a merge. Different merge scenarios may happen, in which some of them cause conflicts that have to be manually resolved. • git push sends our changes to a remote repository. A push can get rejected. 2.7 Exercises 1. Exercise 1. Create an account in your preferred git repository hosting service, create there a repository and clone it. Then, check its history from the command line. How many commits are there in the reposi- tory? Tip: there is a difference if you checked the ”create README.md file” checkbox while creating a repository. 1. Exercise 2. Create a file, a file inside a directory and an empty direc- tory. Commit them (remember, git add, git commit). What can you see there? How does git manage directories? 1. Exercise 3. If you’re on a unix system (linux/osx), try changing your file’s permissions and check git status. How does git treat file per- 15 Getting Started with Git missions? Commit your changes and check the log. What can you ob- serve? 1. Exercise 4. Push now your changes to your remote repository. Then, clone your repository again in another directory. Tip: try checking the help of the clone command git clone -h. Did git save all your files, directories and even permissions? 1. Exercise 5. Go back to your first repository, add a new file, commit it and push it. Then go back to the second repository and pull. Inspect the history in both repositories: Is it the same? 1. Exercise 6. Check your online repository on your hosting service. Can you see the same state as in your local repositories? Go over the dif- ferent tools offered by the hosting, they usually give some idea of the activity of the project, try to understand what they are for. 16 CHAPTER 3 Understanding Git Before going on with the reproducibility concerns that brought you here to read this chapter and even before continuing with practical git commands, we will dive a bit into git concepts. 3.1 Some git Internals Understanding a bit how git works is useful when doing some more compli- cated stuff such as merging and branching. If you already know what is a git commit, a git reference and how the graph of git objects is managed, you can skip this section. Dissecting a git Repository Before starting explaining what is a commit, what is a branch, and so on, let’s start easy by understanding the parts that compose our git repository. When you create a git repository as we did in the last section, or you clone an old repository that already has some files in it, you will find that there is more than meets the eye. A git repository has usually three core collab- orating components: the working copy, the repository, and the remote repositories. You can see an schematics on Figure 3-1. What you usually see in your disk when you clone is not actually the git repository but the working copy. The working copy is the directory where your files are, where you work and apply modifications. It is called a working copy because what you see is actually a copy of what is in the repository. The working copy is a write-able copy: you can freely modify it, break it, add new things or remove them. 17 Understanding Git Your PC github.com remote local remote repository repository Working Copy Figure 3-1 git repository structure: the working copy, the repository, and the remote repositories. Actually, you can do whatever change you want in your working copy: git will not take it into account, at least not automatically. Once your changes are ready, you have to commit them into your repository to store them in your repository. A commit will take your changes, freeze them, and store them in the local database. Just for the curious ones, the local database (also known as the BLOB in the git jargon) is stored inside your working copy, in a hidden directory called .git. The commits you create from your changes live only inside your machine by default. If you want to share your commits with others, or to import commits from some fellow colleague, you have to interact with a remote repository (also called just remote). A remote is a distant git repository that you will synchronize with your local one from time to time. This is where the famous pull and push come into play!. Of course, this is an utterly simplified scenario. You could have a repository without a working copy. And your repository may have many remotes to synchronize with. But we will get into more complex stuff early on, no need to rush now. A history-aware transactional database? As we explained before, we usually work on the working copy, modifying our files and directories. Once we finished some work, we can freeze it and store it in the repository. That’s what we call a commit. From this perspective, a git repository works as a transactional database. You are working on the changes of your disk, but they will not be effectively applied until you finish your transaction. Doing your transaction is per- formed, as in the database world, using the commit command. The result of this transaction is to create a new commit object in the git repository. This commit object will contain an id (usually a hash such as 7ba52e5) plus all changes we wanted to apply. 18 3.1 Some git Internals b8bfed7 7ba52e5 35ac17f b01aba4 Labels: a4153b1 parent Figure 3-2 Graph of commits. git will store your last changes but also remember the entire history of changes you did. It keeps a list of all changes you did so you can do some nice stuff like for example: • come back in time to recover some old changes, • trace the changes in a file to see who (and why!) did a change, or • analyze your repository and do some archeology, to see how your project evolved. It’s a just graph of commits The history of commits we explained before is not stored in a list form but in a graph form. A commit is a node connected to other commits by par- enthood. A commit is said to be parent of another commit if it is the exact previous version. In other words, when we create a new commit, the parent of our new commit is the previous commit. A commit is said to be an ances- tor of another commit if it preceeds it in history. Moreover, a commit can have one or many parents, and many commits can have the same commit as parent. For instance, take a look at the schema of a typical commit graph repre- sented in Figure 3-2. • Commit a4153b1 is the first commit in the graph, with no parents. A commit with no parents represents the first commit in a repository, when no previous history was available. 19 Understanding Git b8bfed7 master_branch HEAD 7ba52e5 v1.0 <tag> 35ac17f b01aba4 feature_branch Labels: reference a4153b1 parent Figure 3-3 git references: a reference refers to a commit. HEAD to a branch and tags are special fixed references. • Commit 35ac17f’s parent is a4153b1 and commit 7ba52e5’s parent is 35ac17f. • Commit b01aba4’s parent is also a4153b1. • Commit b8bfed7 has two parents: 7ba52e5 and b01aba4. You may be asking yourself how can we arrive to such a situation. In short, a commit that is parent of many commits is creating an alternative history line: it is the result of a branch operation. Likewise, a commit that has many parents is joining two histories: it is the result of a merge operation. Naming commits with references You probably noticed that referring to commits by their id is awkward. Com- mit ids are generated automatically as hashes that avoid duplications as much as possible. However, they are not handy to work on a daily basis since they are hard to remember and type. To solve this, git provides a second kind of objects: git references. A git reference is like a label that you put on a commit, to be able to identify that commit by a much simpler name afterwards. For example, you can name a commit as release 1.0 or you can name it as current development commit. As we show in Figure 3-3, there are two main kinds of references in Git: • tags: tags are fixed labels that once created are not meant to be re- moved or moved. They are useful for doing releases: people will expect that a release does not change, otherwise they cannot depend on it. 20 3.2 Understanding Detached HEAD • branches: branches are transferable labels that can be moved from commit to commit. They are used to maintain the different history lines of your project. Another special reference, called HEAD is internally used by git to know what is our current working branch. HEAD usually refers to a branch, not di- rectly a commit. While it would look like an implementation detail, knowing that HEAD is there can save you many headaches as we will see later. What you should see is that master, development are just references too. Now that you have built some strong conceptual git muscles, we can con- tinue in the next sections with some practical Git. Do not hesitate to come back to these sections to refresh some of the basics. As with any sport or dis- cipline, understanding and practicing the basics is really important, since everything else is based on them. 3.2 Understanding Detached HEAD When your project is in a stable state, it is often good to freeze it and put a name to that version. That way, other users can load the frozen version using that well-known name, and also be sure that version will not change. Freezing a version is particularly useful to reproduce a piece of sofware. A frozen version can be reloaded exactly as it is right now but in some point in the future. Thus, software that depends on a frozen version can also benefit from its stability. In git, releasing is done via tags. A tag is a label that we put on a particular commit to be able to find it easily later on, so remember to put short, read- able names to them. One particular consideration about tags is that they are not meant to be modified, although you will find in Git’s documentation that you have special operations (that we do not recommend) to do that. To create a tag, use the command git tag giving as argument a name for the tag and a descriptive message. Usual tag names use semantic version conventions, prefixed with a v. For example version 1 would be v1.0.0. $ git tag -a v1.0.0 -m "First stable release" You can afterwards list all your tags using the git tag command without arguments: $ git tag v0.1.1-alpha v1.0.0 Finally, if you want to recover the code that you tagged at some point, you can use the checkout command with the name of your tag. 21 Understanding Git HEAD v1.0.0 0c0e5ff master Labels: reference 37adf4e parent Figure 3-4 Detached HEAD after checking out a tag: HEAD refers to a commit and not a branch anymore. $ git checkout v1.0.0 Note: checking out 'v1.0.0'. 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 performing another checkout. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example: git checkout -b <new-branch-name> HEAD is now at 0c0e5ff... Adding a title When checking out a tag, git tells you that we are in detached HEAD state. And that whatever commit we do in this state will be lost unless we create a branch. What happened here is that the checkout command modified the HEAD reference to point to the commit pointed by the tag, instead of a branch. Figure 3-4 shows the commit graph for this particular case. When you are in a detached HEAD, if you want to save your modification, you should check out an existing branch using git checkout . 3.3 Merging history lines The most complicated part of git is not branching or commiting, but merg- ing. In our time-travel time-line metaphore we said that branching is equiv- alent to open new time-lines. Merging is the equivalent to join them into a single history. 22 3.3 Merging history lines 2dae910 master development b894b84 dc4a3e7 0c0e5ff Labels: reference 37adf4e parent Figure 3-5 Merging the history with a merge commit. The concept behind merging is not difficult. Using the same idea of graph of commits that we used before, a merge can be represented as a commit that has several parents, thus joining several histories. Figure 3-5 illustrates such a merge commit. However, as you see also in the picture, a merge commit will be referenced by one of the branches but not both. In other words, a merge operation means that a first branch will be merged into a second one. Thus the first one will re- main intact. To perform a merge we need to checkout the branch that will host the changes, and then use the merge command with a branch name as argument. The following example shows how we can merge the development branch into the master branch. $ git checkout master ... $ git merge development [Merge made by the 'recursive' strategy. ... 1 file changed, 0 insertions(+), 0 deletions(-) ...] Managing Conflicts When merging different history lines, things can go wrong if both history lines modified the same file or ressource. Such a problem is also called a con- flict. To understand the issue, let’s generate a conflict on purpose. We can create two branches called future-1 and future-2 adding each the same file but with different contents: 23 Understanding Git $ git checkout -b future-1 $ echo "I'm in future-1" > conflicting.txt $ git add conflicting.txt $ git commit -m "Maybe will cause a conflict" # Let's go back to master and redo the same in another branch $ git checkout master $ git checkout -b future-2 $ echo "I'm in future-2" > conflicting.txt $ git add conflicting.txt $ git commit -m "I'm sure it will cause a conflict!" And then trigger a conflict when trying to merge: # We are in future-2 so we will try to merge future-1 $ git merge future-1 Auto-merging conflicting.txt CONFLICT (add/add): Merge conflict in conflicting.txt Automatic merge failed; fix conflicts and then commit the result. We see that as soon as we merge, git tries to automatically merge the file conflicting.txt. It detects however a merge conflict that does not allow it to continue. If we check Git’s status, you will now see: $ git status On branch future-2 You have unmerged paths. (fix conflicts and run "git commit") Unmerged paths: (use "git add <file>..." to mark resolution) both added: conflicting.txt no changes added to commit (use "git add" and/or "git commit -a") git tells us that conflicting.txt is not merged and that we should fix it. To continue working, we should resolve such a conflict, telling git what ver- sion we want to keep. Several solutions work: either we keep the version we had in future-2, we keep the version incoming from future-1, or we keep a can manually resolve the conflict and keep whatever version we want. The easiest, non-thinking, way to merge is to open the conflicting file and resolve the conflict. For example, if we open our conflicting.txt file with a text editor we will see: <<<<<<< HEAD I'm in future-2 ======= I'm in future-1 >>>>>>> future-1 24 3.4 Commit in workflow Your PC github.com Commit remote local remote repository repository Working Copy Figure 3-6 Commit is an operation that stores things from your working copy into your local repository. git modified our file adding some <<<<<<<, >>>>>>> and ======= markers in our file. What this markers delimit is the conflicts git found. As the first line says, the first region (what is between the <<<<<<< and the =======) corresponds at the version that was in HEAD (i.e., future-2). As the last line says, the last region (what is between the ======= and the >>>>>>>) corre- sponds to the version that was in future-1. To resolve the conflict, you should: • remove all the special markers • keep only the version you want (or edit it to be different) • add and commit the conflicting file For example, let’s say we wanted to keep the version in future-2, we can edit the file leaving only I'm in future-2 and then commit the resolved conflict: $ git add conflicting.txt $ git commit -m "Resolve conflict" 3.4 Commit in workflow Commiting means that we are going to move some content from our working copy to our local repository, as it is shown in Figure 5-1. What the commit command is doing behind is to create a new node in our history graph. Moreover, it will update the master branch label to point to this new commit. The commit graph in this case will look as in Figure 3-7. If we repeat the process, i.e., we apply a change to one of our files, add and commit our commit graph will change again. A new commit with a new 25 Understanding Git 37adf4e master HEAD Labels: reference Figure 3-7 History graph after our first commit. 0c0e5ff master HEAD Labels: reference 37adf4e parent Figure 3-8 History graph after our second commit commit id will be created having as parent our previous commit. The mas- ter branch label will be updated and point to this new commit. The commit graph in this case will look as in Figure 3-8. Notice how our old commit is still there, but it’s accessible as the parent of our new commit. $ git add README.md $ git commit -m "Adding a title" [master 0c0e5ff] Adding a title 1 file changed, 1 insertion(+) 3.5 Creating new history lines with branches Branches in git represent different histories. As in one of science fiction time-travel theories, git branching is equivalent to take one moment in time have several alternative time-lines from there. Figure 3-9 illustrates the idea, showing that you can have two different futures from commit 0c0e5ff. By default, a git repository will include a single branch, called master. Most people only need a single branch to work. However, it may be useful to split work in several branches as we will see later. You can ask git for the branches in the repository using the command git branch -v. 26 3.5 Creating new history lines with branches development master b894b84 dc4a3e7 0c0e5ff Labels: reference 37adf4e parent Figure 3-9 History lines can be branched from a commit. development 0c0e5ff master HEAD Labels: reference 37adf4e parent Figure 3-10 A new branch points by default to the same commit as the current branch. $ git branch -v * master 0c0e5ff Adding a title This command shows all branches in the repository, one per line. Then, for each branch it shows what commit it points, and the comment on that com- mit. Creating a new branch To create a new branch, we can use the command git branch [branch_name] giving as argument the new branch name. This will create a new branch from our current commit, the one that can be resolved from HEAD. Figure 3-10 shows what happens in the graph view. $ git branch development However, as we see in the graph view, creating a new branch does not modify HEAD. Indeed, our current branch/commit did not move. We will observe the 27 Understanding Git same in the command line, if we ask the list of branches. The branch master is marked with a star, indicating it is the actual branch. And both branches point to the same commit. $ git branch -v * master 0c0e5ff Adding a title development 0c0e5ff Adding a title To start working on our new branch, we just need to use the same checkout command we used for tags. $ git checkout development Switched to branch 'development' Or alternatively, we could have created our branch using the checkout -b command, which performs a git branch and a git checkout one after the other. Useful since these operations are usually done together most of the time. # Instead of branch and then checkout $ git checkout -b development Switched to branch 'development' Then, doing some work and creating a commit will only modify our current branch and leave master as it was before. $ touch somefile $ git add somefile $ git commit -m "added somefile" [development b894b84] added somefile 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 somefile $ git branch -v master 0c0e5ff Adding a title * development b894b84 added somefile Diverging history Now that we have done some work in a branch, we can make our branches diverge. We only need to checkout another branch, existing or new, and start working from there. $ git checkout master Switched to branch 'master' $ touch someotherfile $ git add someotherfile $ git commit -m "added someotherfile" [master dc4a3e7] added someotherfile 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 someotherfile $ git branch -v * master dc4a3e7 added someotherfile 28 3.6 Interacting with Remote Repositories development master b894b84 dc4a3e7 0c0e5ff Labels: reference 37adf4e parent Figure 3-11 Divergent history. development b894b84 added somefile This change will create two different history lines, as shown in Figure 3-11. One history line represented by the master branch, and another history line represented by the development branch. 3.6 Interacting with Remote Repositories So far we have worked only on the repository that resides locally in our ma- chine. This means that mostly all of git features are available without re- quiring an internet connection, making it suitable for working off-line. How- ever, working off-line is a two-edged sword: all your changes are captive in your machine. While your changes are in your machine, nobody else can contribute or collaborate to them. Moreover, losing your machine would mean losing all your changes too. Keeping your changes safe means to synchronize them from time to time with a remote repository. A remote repository is a copy of your local reposi- tory that is stored remotely, that is, in somewhere else’s machine. This could be, for example, in your company’s or university’s server, the cloud, etc. In this section we will see how to interact with remotes, how to configure them, and how to synchronize our local repository with them. git Remotes A git remote is a git server that is hosted in some machine other than ours. Usually, a remote will be hosted by some company like GitHub or GitLab, but it can be hosted also within our own company/university/research labora- tory. Actually, we have already worked with a remote without knowing it, when we have cloned our repository in Section 2.2. The code we used in that moment was: 29 Understanding Git $ git clone git@github.com:[your_username]/[your_repo_name].git Which can be generalized as: $ git clone [remote] Once created, we can interrogate our repository for its remotes using the command git remote -v. We will then observe that git created automati- cally a remote named origin pointing to the location that we just cloned. $ git remote -v origin git@github.com:[your_username]/[your_repo_name].git (fetch) origin git@github.com:[your_username]/[your_repo_name].git (push) This first means that git allows us to assign a name to avoid using urls all the way. In addition, we can see that git differentiates remotes used for fetching from those used for pushing. Those differences are important for more advanced git configuration, that we will not cover in this chapter. Adding and Removing Remotes For advanced scenarios, when we need more than the default origin remote, we will need to use different remotes. All git commands interacting with a remote repository will have a variant accepting a remote repository as argu- ment, as we will see later. In those cases, we can specify the remote’s url on each of those commands to interact with the desired remote. However, to avoid copy-pasting different remote urls all the time, git pro- vides us with the possibility of configuring new named remotes such as ori- gin. The drawback of such an approach is that our list of remotes will need to be maintained from time to time, for example, if urls become invalid or our repository moves. In such cases, we will want to modify or remove old remotes to keep avoid errors or mistakes. To create a new named remote we can execute the command git remote add [remote_name] [url]. $ git remote add someRemote [url] $ git remote -v origin git@github.com:[your_username]/[your_repo_name].git (fetch) origin git@github.com:[your_username]/[your_repo_name].git (push) someRemote [url] (fetch) someRemote [url] (push) Existing remotes can then be renamed using the git remote rename [old_name] [new_name]. And in case the remote name you wanted to rename does not exist, git will answer you with a falta error. $ git remote rename someRemote company_remote $ git remote rename non_existent newname fatal: No such remote: non_existent 30 3.6 Interacting with Remote Repositories Existing remotes can then be renamed using the git remote rename [old_name] [new_name]. And in case the remote name you wanted to rename does not exist, git will answer you with a falta error. $ git remote rename someRemote company_remote $ git remote rename non_existent newname fatal: No such remote: non_existent Finally, to remove an existing named remote you can use the git remote remove [remote_name]. And in case the remote name you wanted to re- name does not exist, git will answer you with a falta error. $ git remote remove company_remote $ git remote remove non_existent fatal: No such remote: non_existent Update your repository: Fetching and Pulling Before being able to share our commits in some external server, we need be- fore to update our repository to avoid them being out of synchronization. While you can always try to share your commits by pushing (see Section 3.6), you will see with experience that git favors pulling before pushing. This is, among others, because in your local repository you can do whatever manip- ulation you want to solve mistakes and merge conflicts, while you cannot do the same in your remote repository. Concretely, when using git you have to have a state of mind where: 1. . you update your repository 2. . you fix locally whatever existing conflict between your work and the remote work 3. . you then publish your changes. Note Actually, our recommended workflow has one more step before updating: commit. If you try to update when your working copy is dirty, updating can destroy your changes. Instead, if you commit before doing an update, your changes will be safely stored in the database. You’ll be able to do any expert manipulation with your changes once they are in the repository. As we said before, a git repository is no other than a database. It is a database that stores commits and references to those commits. And to update this database, we require two basic operations: • fetch. Bring the commits and references from a remote repository to your local repository without affecting your own. • merge. Merge the remote references with your own references, the same operation explained in Section 3.3. 31 Understanding Git Your PC pull fetch github.com merge local remote Working Copy repository remote repository Figure 3-12 Fetch is an operation that brings things from a remote into your lo- cal repository. Merge will join the remote history with your current history and update your working copy. Pull will do both of them. In addition, the pull operation does both fetch and merge in a single opera- tion (Figure 3-12). Fetching is done through the git fetch [remote] command, where we can specify both a remote url or a remote name as remote. Or, if we don’t specify a remote, git will by default fetch from whatever remote is specified as origin. Executing a fetch will show an output like the following: $ git fetch [remote_name] remote: Counting objects: 79, done. remote: Compressing objects: 100% (23/23), done. remote: Total 79 (delta 52), reused 74 (delta 52), pack-reused 4 Unpacking objects: 100% (79/79), done. From git://github.com/[project_owner]/[your_repo_name] 6b52ae6..5c53245 development -> [remote_name]/development * [new branch] issue/876 -> [remote_name]/issue/876 * [new tag] v1.0 -> v1.0 Indeed, fetch will bring some objects (e.g., commits) to our repository, bring new branches and so on, but it will not update any of your branches or tags. We can then procceed to merge our local branch with the one in the remote by doing a normal merge operation but indicating a remote branch (that is, a branch prefixed by its remote name). Of course, as any merge operation, this can incurr into a conflict, that we should fix locally before continuing. $ git merge [remote_name]/master [Merge made by the 'recursive' strategy. ... 1 file changed, 10 insertions(+), 1 deletions(-) ...] These both operations could have been replaced by a git pull [remote_name] [branch_name] command. Pulling will fetch all commits from the branch named [branch_name] in the remote [remote_name] and then merge those 32 3.6 Interacting with Remote Repositories Your PC push github.com commit local remote Working Copy repository remote repository Figure 3-13 Push is an operation that sends commits from your local repository to a remote repository. commits with your current branch. Share your commits: Pushing The final step in our git journey is to share our changes to the world. Such sharing is done by pushing commits to a remote repository, as shown in Fig- ure 3-13. To push, you need to use the git command git push [remote] [remote_branch]. This command will send the commits pointed from your your current branch to the remote [remote] in the branch [remote_branch]. $ git push origin temp Counting objects: 3, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 271 bytes | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@github.com:[your_username]/[your_repo_name].git b6dcc3f..f269295 master -> temp To avoid specifying the remote and destination branch on every push (which may be a bit verbose), you can avoid those parameters and rely on git de- fault values. By default the git push operation will try to push to the branch’s upstream. A branch’s upstream is the per-branch configuration saying to which remote/branch pair it should push by default. When we clone a repos- itory, the default branch comes with an already configured upstream. We can interrogate git for the branch’s upstreams with the super verbose flag in the branch command, i.e., git branch -vv, where we can see for example that our master branch’s upstream is origin/master, while our develop- ment branch has no upstream. $ git branch -vv # doubly verbose! development 1656797 This commit adds a new feature master f269295 [origin/master] First commit 33 Understanding Git On the other side, when a branch has no upstream, a push operation will by default fail with a git error. git will ask us to set an upstream, or otherwise specify a pair remote/branch for each push. $ git push fatal: The current branch test has no upstream branch. To push the current branch and set the remote as upstream, use git push --set-upstream origin test $ git push --set-upstream origin test Counting objects: 3, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 271 bytes | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@github.com:[your_username]/[your_repo_name].git b6dcc3f..f269295 master -> test Finally, another thing may happen while pushing: git may reject our changes. $ git push To git@github.com:guillep/test.git ! [rejected] master -> master (fetch first) error: failed to push some refs to 'git@github.com:[your_username]/[your_repo_name].git' hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first integrate the remote changes hint: (e.g., 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details. As the error message says, the remote has changes that we do not have lo- cally, so we need to update our repository first. This can be solved with a pull and a merge. 3.7 Exercises 1. Exercise 1. Get a repository with many commits and checkout the par- ent of the current commit. This will put you in ”Detached HEAD” state. Solve it using a new branch. 1. Exercise 2. Try to merge your previous branch into your new branch. What kind of merge is it? 34 3.7 Exercises 1. Exercise 3. Repeat the scenario of the first exercise, apply a change to your one of your files and commit it. Try to merge your previous branch into your new branch. What kind of merge is it? 1. Exercise 4. Create a new online repository and push your changes into it. 1. Exercise 5. What is the smaller set of steps you could imagine to create a conflict? 35 CHAPTER 4 Practical Git Scenarios In this chapter we will show some git features that can help you in your daily use of git. 4.1 Before commit little helpers You may add too fast a file to your staging area or index. Or you may want to discard the changes you did to your working copy to get back in the situation where no changes were made. The two problems can be handled as follows. Remove Files from the Staging Area It may happen that you went too fast and added a file to your staging area (remember the groceries’ list) and that you changed your mind and do not want to add it anymore. Your file is not committed so a simple reset will do the job. You can either remove single files or everything. $ git reset HEAD file # Or everything $ git reset HEAD . Getting back your file as in your local repository Another common scenario is that you modified a file of your working copy and your realize that it would be better to drop the changes and get back the file that is versioned in your local repository. Simply checkouting again will fix your problem $ git checkout file 37 Practical Git Scenarios 4.2 Exploring the History The git log Command The commit graphs we have shown so far are not evident at all while when we use the git status command. There is however a way to ask git about them using the git log command. $ git log commit 0c0e5ff55b56fe8eabc1661a1da64b41f9d74472 Author: Guille Polito <guillermopolito@gmail.com> Date: Wed Mar 21 15:37:32 2018 +0100 Adding a title commit 37adf4eaa945cbd7460991f88bff5aa902db06ce Author: Guille Polito <guillermopolito@gmail.com> Date: Wed Mar 21 14:02:43 2018 +0100 first version git log prints the list of commits in order of parenthood. The one on the top is the most recent commit, our last commit. The one below is its par- ent, and so on. As you can see, each commit has an id, the author name, the timestamp and its message. To display a more compact version (commit ids + message) of the log use git log --oneline We can also ask git what are the changes introduced in a particular commit using the command git show. $ git show 0c0e5ff55b56fe8eabc1661a1da64b41f9d74472 commit 0c0e5ff55b56fe8eabc1661a1da64b41f9d74472 Author: Guille Polito <guillermopolito@gmail.com> Date: Wed Mar 21 15:37:32 2018 +0100 Adding a title diff --==git== a/README.md b/README.md index e69de29..cad05f1 100644 --- a/README.md +++ b/README.md @@ -0,0 +1 @@ +! a title \ No newline at end of file That will give us the commit description as in git log plus a (not so read- able) diff of the modified files showing the inserted, modified and deleted 38 4.2 Exploring the History Figure 4-1 Example of SourceTree’s commit graph view. lines. More advanced graphical tools are able to read this description and show a more user-friendly diff. Accessing the History Graph Git’s log provides a more graphish view on the terminal using some cute ascii art. This view can be accesses through the git log --graph --oneline --all command. Here is an example of this view for a more complex project. In this view, stars represent the commits with their ids and commit mes- sages, and lines represent the parenthood relationships. $ git log --graph --oneline --all * 4eb8446 Documenting * e5a3e2e Add tests * 680a79a Some other | * ed4854f Merge pull request #1137 | |\ | | * 9e30e37 Some feature | * | ba7f65c Merge pull request #1138 | |\ \ | | * | 31a40c4 Some Enhancement | | |/ | * | 2d4698d Merge pull request #1139 | |\ \ | | * | 20c0ff4 Some fix | | |/ | * | ae3ec45 Merge pull request #1136 However, we are not always in the mood of using the terminal, or of wanting to decode what was done in ascii art. There are tools that are more suitable to explore the history of a project, usually providing some nice graphical ca- pabilities. This is the case of tools such as SourceTree (Figure 4-1) or Github’s network view (Figure 4-2). 39 Practical Git Scenarios Figure 4-2 Example of Github’s commit graph view. 4.3 Discarding your Local Committed Changes It comes the time for every woman/man to make mistakes and want to dis- card them. Doing so may be dangerous, since once discarded you will not able to recover your changes. It is however possible to instruct git to do so. For it, there are two git comments that will perform the task for you and when combined they will completely discard every dirty file and directory in your repository: git reset and git clean. $ git reset --hard <commit_id> $ git clean -df The -d option removes untracked directories in addition to untracked files, while the -f option is a shortcut --force, forcing the corresponding dele- tions. The reason for needing two commands instead of one relies on the fact that git has several staging areas (such as the ones used to keep the tracked files), which we usually would like to clean when we discard the repository. Of course, experienced readers may search why they would need both in Git’s documentation. 4.4 Ignoring Files Many times we will find that we do not want to commit some files that are in our repository’s directory. This is mostly the case of generated or auto- matically downloaded files. For example, imagine you have a C project and some makefiles to compile it, generating a binary library. While it would be good to store the result of compilation from time to time, storing it in a git repository (or SVN, or Bazar) may be a cause of headaches. First, as you will see in 3, this may be a cause for conflicts. Second, since we should be able to 40 4.5 Commiting a File Filtered out by the .gitignore generated such binary library from the sources, having the already compiled result in the repository does not add so much value. This same ideas can be used to ignore any kind of generated file. For exam- ple, pdfs generated by document generation tools, meta-data files generated by IDEs and tools (e.g., Eclipse), compiled libraries (e.g., dll, so, or dylib files). In such cases, we can tell git to ignore cetain files using the .gitignore file. The .gitignore file is an optional text file that we can write in the root of our repository with a list of file paths to ignore. # Example of .gitignore file # Lines starting with hashtags are comments # A file name will ignore that file someignoredfile.txt # A file name will ignore that file someignoredfile.txt # A file pattern will ignore all pdf files *.pdf Once your file is ready, you have to add it and commit it to git to make it take effect. $ git add .gitignore $ git commit -m "Added gitignore" From this moment on, all listed files will be ignored by git add and git status. And you will be able to perform further commands to add ”all but ignored files”: $ git add . Note If a file or a file type is tracked but you want git to ignore its changes afterward, adding it to .gitignore file will not make the job i.e. git will continue to track it. To avoid keeping track of it in the future, but secure it locally in your work- ing directory, it must be removed from the tracking list using ==git rm –cached <file> (.<file_type>)==. Nevertheless, be aware that the file is still present in the past history! 4.5 Commiting a File Filtered out by the .gitignore For certain workflow, it is better to filter out certain files such as generated pdf but from time to time you may want to version a file that would not be 41 Practical Git Scenarios because its extension or folder is listed in the .gitignore file. You can still commit such a such without having to touch the .gitignore file. You can use the --force option of the add command. $ git add -f that.pdf $ git commit -m "that file is still important" 4.6 Getting out of Detached HEAD Detached head means no other than ”HEAD is not pointing to a branch”. Be- ing in a detached HEAD state is not bad in itself, but it may provoke loss of changes. As a matter of fact, any commit that is not properly referenced by another commit or by another git reference (tag, branch) may be garbage collected. git will not forbid you to commit in this state, but any new commit you cre- ate will only reachable if you remember the commit hash. To get out of det- tached HEAD, the easiest solution is to checkout a branch, as we will see in the next section. Checking out a branch will set HEAD to point to a branch instead of a commit, saving you some HEADaches. 4.7 Accessing your Repository through SSH To be able to access your repository from your local machine, you need to setup your credentials. Think it this way: you need to tell the server who you are on every interaction you have with it. Otherwise, Github will reject any operation against your repository. Such a setup requires the creation and uploading of SSH keys. An SSH key works as a lock: a key is actually a pair of a public and a private key. The private key is meant to reside in your machine and not be published at all. A public key is meant to be shared with others to prove your identity. Whenever you want to prove your identity, SSH will exchange messages en- crypted with your public key, and see if you are able to decrypt it using your private key. To create an SSH key, in *nix systems you can simply type in your terminal $ ssh-keygen -t rsa -b 4096 -C "your_email@some_domain.com" Follow the instructions in your terminal such as setting the location for your key pair (usually it is $HOME/.ssh) and the passphrase (a kind of password). Finally, you’ll end up with your public/private pair on the selected location. It is now time to upload it to Github. Connect yourself to your Github settings (usually https://github.com/set- tings/profile) and go to the ”SSH and GPG keys” menu. Import there the con- tents of your public key file. You should be now able to use your repository. 42 4.8 Rewriting the History 4.8 Rewriting the History Many times it happens that we accidentally commit something wrong. Maybe we wanted to commit more or less things, maybe a completely different con- tent, or we did a mistake in the commit’s message. In these cases, we can rewrite Git’s history, e.g, undo our current commit and go back to the previ- ous commit, or rewrite the current commit with some new properties. Be careful! Rewriting the history can have severe consequences. Imagine that the commit you want to undo was already pushed. This means that somebody else could have pulled this commit into her/his repository. If we undo this already published commit, we are making everybody else’s repos- itories obsolete! This can be indeed problematic depending on the number of users the project has, and their knowledge on git to be able to solve this issue. Undo a commit using git reset --hard To undo the last commit, it is as easy as: $ git reset --hard HEAD~1 git reset --hard [commitish] makes your current branch point to [com- mitish]. HEAD is your current head, and you can read ~1 as ”minus one”. In other words, HEAD~1 is head minus one, which boils down to the parent of head, our previous commit. You can use this same trick to rewrite the history in any other way, since you can use any commitish expression to reset. For example, HEAD~17 means 17 versions before head, or someBranch~4 means four commits before the branch someBranch. Update a Commit’s Message using git commit --amend To change our current commit’s message you can use the following com- mand: $ git commit --amend -m "New commit message" Or, if you don’t use the -m option, a text editor will be prompt so you can edit a commit message. $ git commit --amend You can use the same trick not only to modify a commit’s message but to modify your entire commit. Actually, just adding new things with git add before an --amend will replace the current commit with a new commit merg- ing the previous commit changes with what you just added. 43 Practical Git Scenarios 4.9 How to Overwrite/Modify Commits WARNING: It is highly not recommended to rewrite the history of a repo es- pecially when part of it has already been pushed to a remote. Modifying the history will most likely break the history shared by the different collabora- tors and you may deal with an inextricable merge conflict. Change the last Commit Imagine you have just committed your changes and have not pushed them yet, but 1. you are not satified with the commit message $ git log --oneline $ git commit --amend -m "Updated commit message" $ git log --oneline 2. you forgot to save some modification or to add some files before commit- ting. Then make your changes and use $ git commit -a --amend --no-edit Merge two commits First, it is worth repeating that you must think twice before modifying the history of the repo. Now, assume that you have not pushed the correspond- ing commits. Merging two consecutive commits is a way to work with a cleaner tree. Consider you want to merge commits ”Intermediate” and ”Old” $ git log --oneline eae7846 New 71c0c64 Intermediate f039832 Old cca92f1 Even older Then, you can interactively -i focus on the last three HEAD~3 commit. $ git rebase -i HEAD~3 pick f039832 Old pick 71c0c64 Intermidiate pick eae7846 New Note Observe that the commits are displayed in the reversed order. Now you can squash the commit ”Intermediate” into its parent commit ”Old” 44 4.9 How to Overwrite/Modify Commits pick f039832 Old squash 71c0c64 Intermidiate pick eae7846 New And set the message e.g. ”Merge intermediate + old” attached to the single. # This is a combination of 2 commits. # This is the 1st commit message: Old # This is the commit message #2: Intermediate # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. $ git log --oneline eae7846 New 651375a Merge intermediate + old cca92f1 Even older Note that the commit id has changed Pushing Rewritten History As soon as the history we have rewritten was never pushed before, we can continue working normally and pushing our changes then without prob- lems. However, if we have already pushed the commit we want to undo, this means that we are potentially impacting all users of our repository. Because of the problems it can pose to other people, pushing a rewritten history is not a completely favoured by git. Better said, it is not allowed by default and you’ll be warned about it: $ git push To git@github.com:REPOSITORY_OWNER/YOUR_REPOSITORY.git ! [rejected] YOUR_BRANCH -> YOUR_BRANCH (non-fast-forward) error: failed to push some refs to 'git@github.com:REPOSITORY_OWNER/YOUR_REPOSITORY.git' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Integrate the remote changes (e.g. hint: '==git== pull ...') before pushing again. hint: See the 'Note about fast-forwards' in '==git== push --help' for details. With this message git means that you should not blindly overwrite the his- tory. Also, it suggests to pull changes from the remote repository. How- ever, doing that will bring back to our repository the history we wanted to 45 Practical Git Scenarios undo! What we want to do is to impose our current (undone) state in the re- mote repository. To do that, we need to force the push using the git push --force or the git push -f option. $==git== push -f Total 0 (delta 0), reused 0 (delta 0) To git@github.com:REPOSITORY_OWNER/YOUR_REPOSITORY.git + a1713f3...6e0c7bf YOUR_BRANCH -> YOUR_BRANCH (forced update) 4.10 Conclusion This chapter presented some operations that may help you. As a general principle avoid rewriting history because you may lose changes and get into unrecovable state. 46 CHAPTER 5 Publishing your first Pharo project with Iceberg In this chapter we explain how you can publish your project on Github using Iceberg. We do not explain basic concepts like commit, push/pull, merging, or cloning. A strong precondition before reading this chapter is that you must be able to publish from the command line to the git hosting service that you want to use. If you cannot do not expect Iceberg to fix it magically for you. Now if you have some problems with SSH configuration (which is the default with Github) you can either use HTTPS or have a look in Chapter 9 the section ex- plaining how to generate SSH keys. Let us get started. 5.1 For the impatient If you do not want to read everything, here is the executive summary. • Create a project on Github or any git-based platform. • [Optional] Configure Iceberg to use custom ssh keys. • Add a project in Iceberg. – Optionally but strongly recommanded, in the cloned repository, create a directory named src on your file system. This is a good convention. • In Iceberg, open your project and add your packages. • Commit your project. 47 Publishing your first Pharo project with Iceberg Your PC github.com Commit remote github repository repository Working Copy Figure 5-1 A distributed versioning system. • [Optional] Add a baseline to ease loading your project. • Push your change to your remote repository. You are done. Now we can explain calmly. 5.2 Basic Architecture As git is a distributed versioning system, you need a local clone of the repos- itory and a working copy. Your working copy and local repository are usually on your machine. This is to this local repository that your changes will be commited to before being pushed to remote repositories (Figure 5-1). We will see in the next Chapter that the situation is a bit more complex and that Ice- berg is hiding the extra complexity for us. 5.3 Create a new project on Github While you can save locally first and then later create a remote repository, in this chapter we first create a new project on Github. Figure 5-2 shows the creation of a project on Github. The order does not really matter. What is different is that you should use different options when add a repository to Iceberg as we will show later. 5.4 [Optional] SSH setup: Tell Iceberg to use your keys To be able to commit to your git project, you should either use HTTPS or you will need to set up valid credentials in your system. In case you use SSH (the default way), you will need to make sure those keys are available to your Github account and also that the shell adds them for smoother communica- tion with the server. See the Chapter 9 Tips and Tricks for some help with setting up your ssh keys. Go to settings browser, search for ”Use custom SSH keys” and enter your data there as shown in Figure 5-3). 48 5.4 [Optional] SSH setup: Tell Iceberg to use your keys Figure 5-2 Create a new project on Github. Figure 5-3 Use Custom SSH keys settings. Alternatively, you can execute the following expressions in your image play- ground or add them to your Pharo system preference file (See Menu System item startup): IceCredentialsProvider useCustomSsh: true. IceCredentialsProvider sshCredentials publicKey: 'path\to\ssh\id_rsa.pub'; privateKey: 'path\to\ssh\id_rsa' Note Pro Tip: this can be used too in case you have a non-default key file. You just need to replace id_rsa with your file name. 49 Publishing your first Pharo project with Iceberg Figure 5-4 Iceberg Repositories browser on a fresh image indicates that if you want to version modifications to Pharo itself you will have to tell Iceberg where the Pharo clone is located. But you do not care. 5.5 Iceberg Repositories browser Figure 5-4 shows the top level Iceberg pane. It shows that for now you do not have defined nor loaded any project. It shows the Pharo project and indi- cates that it could not find its local repository by displaying ’Local repository missing’. First you do not have to worry about the Pharo project or repository if you do not want to contribute to Pharo. So just go ahead. Now if you want to un- derstand what is happening here is the explanation. The Pharo system does not have any idea where it should look for the git repository corresponding to the source of the classes it contains. Indeed, the image you are executing may have been built somewhere, patched or not many times. Now Pharo is fully operational without having a local repository. You can browse system classes and methods because Pharo has its own internal source management. This warning just indicates that if you want to version Pharo system code us- ing git then you should indicate to the system where the clone and working copy are located on your local machine. So if you do not plan to modify and version the Pharo system code, you do not have to worry. 5.6 Add a new project to Iceberg The first step is then to add a project to Iceberg: • Press the ’+’ button to the right of the Iceberg main window. • Select the source of your project. In our example, since you did not clone your project yet, choose the Github option. Notice that you can either use SSH (Figure 5-5) or HTTPS (Figure 5-6). Figure 5-5 and 5-6) instruct Iceberg to clone the repository we just created on Github. We specify the owner, project, and physical location where the local 50 5.6 Add a new project to Iceberg Figure 5-5 Cloning a project hosted on Github via SSH. Figure 5-6 Cloning a project hosted on Github via HTTPS. clone and git working copy will be on your disk. Iceberg has now added your project to its list of managed projects and cloned an empty repository to your disk. You will see the status of your project, as in Figure 5-7. Here is a breakdown of what you are seeing: • MyCoolProjectWithPharo has a star and is green. This usually means that you have changes which haven’t been committed yet, but may also happen in unrelated edge cases like this one. Don’t worry about this for now. • The Status of the project is ’No Project Found’ and this is more impor- tant. This is normal since the project is empty. Iceberg cannot find its metadata. We will fix this soon. Later on, when you will have commited changes to your project and you want to load it in another image, when you will clone again, you will see that 51 Publishing your first Pharo project with Iceberg Figure 5-7 Just after cloning an empty project, Iceberg reports that the project is missing information. Figure 5-8 Adding a project with some contents shows that the project is not loaded - not that it is not found. Iceberg will just report that the project is not loaded as shown in Figure 5-8. 5.7 Repair to the rescue Iceberg is a smart tool that tries to help you fix the problems you may en- counter while working with git. As a general principle, each time you get a status with red text (such as ”No Project Found” or ”Detached Working Copy”), you should ask Iceberg to fix it using the Repair command. Iceberg cannot solve all situations automatically, but it will propose and ex- plain possible repair actions. The actions are ranked from most to least likely to be right one. Each action has a displayed explanation on the situation and the consequences of the using it. It is always a good idea to read them. Set- ting your repository the right way makes it extremely hard to lose any piece of code with Iceberg and Pharo is general since Pharo contains its own copy of the code. 52 5.8 Create project metadata Figure 5-9 Create project metadata action and explanation. Figure 5-10 Showing where the metadata will be saved and the format encod- ings. 5.8 Create project metadata Iceberg reported that it could not find the project because some meta data were missing such as the format of the code encodings and the example lo- cation inside the repository. When we activate the repair command we get Figure 5-9. It shows the ”Create project metadata” action and its explana- tion. When you choose to create the project metadata, Iceberg shows you the filesystem of your project as well as the repository format as shown in Figure 5-10. Tonel is the preferred format for Pharo projects. It has been designed to be Windows and file system friendly. Change it only if you know what you are doing! Before accepting the changes, it is a good idea to add a source (src) folder 53 Publishing your first Pharo project with Iceberg Figure 5-11 Adding a src repository for code storage. Figure 5-12 Resulting situation with a src folder. to your repository. Do that by pressing the + icon. You will be prompted to specify the folder for code as shown in Figure 5-11. Iceberg will show you the exact structure of your project as shown in Figure 5-12. After accepting the project details, Iceberg shows you the files that you will be committing as shown in Figure 5-13 Once you have committed the metadata, Iceberg shows you that your project has been repaired but is not loaded as shown in Figure 5-8. This is normal since we haven’t added any packages to our project yet. You can optionally push your changes to your remote repository. Your local repository is ready, let’s move on to the next part. 5.9 Add and commit your package using the Working copy browser Once your project contains Iceberg metadata, Iceberg will be able to manage it easily. Double click on your project to bring a Working copy browser for your project. It lists all the packages that compose your project. Right now you have none. Add a package by pressing the + (Add Package) iconic button as shown by Figure 5-14. 54 5.9 Add and commit your package using the Working copy browser Figure 5-13 Details of metadata commit. Figure 5-14 Adding a package to your project using the Working copy browser. Again, Iceberg shows that your package contains changes that are not com- mitted using the green color and the star in front of the package name as showing in Figure 5-15. Commit the changes Commit the changes to your local repository using the Commit button as shown in Figure 5-16. Iceberg lets you chose the changed entities you want to commit. Here this is not needed but this is an important feature. Iceberg will show the result of the commit action by removing the star and changing the color. It now shows that the code in the image is in sync with your local repository as shown by Figure 5-17. You can commit several times if needed. 55 Figure 5-15 Iceberg indicates that your package has unsaved changes – indeed you just added your package. Figure 5-16 When you commit changes, Iceberg shows you the code about to be committed and you can chose the code entities that will effectively be saved. Figure 5-17 Once changes committed, Iceberg reflects that your project is in sync with the code in your local repository. 5.10 Conclusion Figure 5-18 Publishing your committed changes. Publish your changes to your remote Now you are nearly done. Publish your changes from your local directory to your remote repository using the Push button. You may be prompted for credentials if you used HTTPS. When you push your changes, Iceberg will show you all the commits awaiting publication and will push them to your remote repository as shown in Figure 5-18. The figure shows the commits we are about to make to add a baseline, which will allow you to easily load your project in other images. Now you are basically done. 5.10 Conclusion You now know the essential aspects of managing your code with Github. Ice- berg has been designed to guide you so please listen to it unless you really know what you are doing. You are now ready to use services offered around Github to improve your code control and quality! 57 CHAPTER 6 Configure your project nicely Versioning code is just the first part of making sure that you and others can reload your code. In this chapter we describe how to define a baseline, a project map that you will use to define dependencies within your project and dependencies to other projects. We also show how to add a good .git- ignore file. In the next chapter we will show how to configure your project to get more out of the services offered within the Github ecosystem such as Travis-ci to execute automatically your tests. We start by showing you how you can commit your code if you did not create your remote repository first. Figure 6-1 Creating a local repository without pre-existing remote repository. 59 Configure your project nicely Figure 6-2 Opening the repository browser let you add and browse branches as well as remote repositories. 6.1 What if I did not create a remote repository In the previous chapter we started by creating a remote repository on Github. Then we asked Iceberg to add a project by cloning it from Github. Now you may ask yourself what is the process to publish first your project locally without a pre-existing repository. This is actually simple. Create a new repository. When you add a new repository use the ’New repository’ option as shown in 6-1. Add a remote. If you want to commit to a remote repository, you will have to add it using the Repository browser. You can access this browser through the associated menu item or the icon. The Repository browser gives you access to the git repositories associated with your project: you can access, manage branches and also add or remove remote repositories. Figure 6-3 shows the repository browser on our project. Pressing on the ’Add remote’ iconic button adds a remote by filling the needed information that you can find in your Github project. Figure 6-3 shows it for the sample project using SSH and Figure 6-4 for HTTPS. Push to the remote. Now you can push your changes and versions to the remote repository using the Push iconic button. Once you have pushed you can see that you have one remote as shown in Figure 6-5. 60 Figure 6-3 Adding a remote using the Repository browser of your project (SSH version). Figure 6-4 Adding a remote using the Repository browser of your project (HTTP version). Figure 6-5 Once you pushed you changes to the remote repository. Configure your project nicely Figure 6-6 Added the baseline package to your project using the Working copy browser. 6.2 Defining a BaselineOf A baseline is a description of the architecture of a project. You will express the dependencies between your packages and other projects so that all the dependent projects are loaded without the user having to understand them or the links between them. A baseline is expressed as a subclass of BaselineOf and packaged in a pack- age named 'BaselineOfXXX' (where ’XXX’ is the name of your project). So if you have no dependencies, you can have something like this. BaselineOf subclass: #BaselineOfMyCoolProjectWithPharo ... package: 'BaselineOfMyCoolProjectWithPharo' BaselineOfMyCoolProjectWithPharo >> baseline: spec <baseline> spec for: #common do: [ spec package: 'MyCoolProjectWithPharo' ] Once you have defined your baseline, you should add its package to your project using the working copy browser as explained in the previous chapter. You should obtain the following situation shown in Figure 6-6. Now, commit it and push your changes to your remote repository. A more elaborated web resources about baseline possibility is available at: https://github.com/pharo-open-documentation/pharo-wiki/. 62 6.3 Loading from an existing repository 6.3 Loading from an existing repository Once you have a repository you committed code to and would like to load it into a new Pharo image, there are two ways to work this out. Manual load. • Add the project as explained in the first chapter • Open the working copy browser by double clicking on the project line in the repositories browser. • Select a package and manually load it. Scripting the load. The second way is to make use of Metacello. However, this will only work if you have already created a BaselineOf. In this case, you can just execute the following snippet: Metacello new baseline: 'MyCoolProjectWithPharo'; repository: 'github://Ducasse/MyCoolProjectWithPharo/src'; load For projects with metadata, like the one we just created, that’s it. Notice that we not only mention the Github pass but also added the code folder (here src). 6.4 [Optional] Add a nice .gitignore file Iceberg automatically manages files such as .gitignore. # For Pharo 70 and up # http://www.pharo.org # Since Pharo 70 all the community is moving to git. # image, changes and sources *.changes *.sources *.image # Pharo Debug log file and launcher metadata PharoDebug.log pharo.version meta-inf.ston # Since Pharo 70, all local cache files for Monticello package cache, playground, epicea... are under the pharo-local 63 Configure your project nicely /pharo-local # Metacello-github cache /github-cache github-*.zip 6.5 Going further: Understanding the architecture As git is a distributed versioning system, you need a local clone of your repository. In general you edit your working copy located on your hard- drive and you commit to your local clone, and from there you push to remote repositories like Github. We explain here the specificity of managing Pharo with git. When coding in Pharo, you should understand that you are not directly edit- ing your local working copy, you are modifying objects that represent classes and methods that are living in the Pharo environment. Therefore it is like you have a double working copy: Pharo itself and the git working copy. When you use git command lines, you have to understand that there is the code in the image and the code in the working copy (and your local clone).To update your image, you first have to update your git working copy and then load code from the working copy to the image. To save your code you have to save the code to files, add them to your git working copy and commit them to your clone. Now the interesting part is that Iceberg manages all this for you transpar- ently. All the synchronization between these two working copies is done be- hind the scene. Figure 6-7 shows the architecture of the system. • You have your code in the Pharo image. • Pharo is acting as a working copy (it contains the contents of the local git repository). • Iceberg manages the publication of your code to the git working copy and the git local repository. • Iceberg manages the publication of your code to remote repositories. • Iceberg manages the re-synchronization of your image with the git local repository, git remote repositories and the git working copy. 6.6 Conclusion We show how to package your code correclty. It will help you to reload it and use the services that we will present in the next chapter. 64 Figure 6-7 Architecture. CHAPTER 7 Empowering your projects Now that you can save your code on Github in a breeze, you can take ad- vantage of services to automate actions. For example, to execute tests using Travis1 ) or AppVeyor2 for Windows. 7.1 Adding Travis integration By adding two simple files, you can have the tests of your project automat- ically run after each commit with Travis. You need to enable Travis in your Github repository. Since Travis is changing its policy and work tightly in re- lation with Github you might have a better time checking on their respective websites. You should also add the two following files: .travis.yml and .smalltalk.ston in the top level of your repository. .travis.yml language: smalltalk sudo: false os: - linux smalltalk: - Pharo-7.0 .smalltalk.ston 1 http://www.travis-ci.com 2 https://www.appveyor.com 67 Empowering your projects SmalltalkCISpec { #loading : [ SCIMetacelloLoadSpec { #baseline : 'MyCoolProjectWithPharo', #directory : src', #platforms : [ #pharo ] } ] } If you have done everything right, Travis will pick up the changes and will start testing and building your project… and you are done, congratulations! 7.2 On windows If you want to make sure that your code runs on windows, you should use the Appveyor service and add the appveyor.yml file. environment: CYG_ROOT: C:\cygwin CYG_BASH: C:\cygwin\bin\bash CYG_CACHE: C:\cygwin\var\cache\setup CYG_EXE: C:\cygwin\setup-x86.exe CYG_MIRROR: http://cygwin.mirror.constant.com SCI_RUN: /cygdrive/c/smalltalkCI-master/run.sh matrix: - SMALLTALK: Pharo-6.1 - SMALLTALK: Pharo-7.0 platform: - x86 install: - '%CYG_EXE% -dgnqNO -R "%CYG_ROOT%" -s "%CYG_MIRROR%" -l "%CYG_CACHE%" -P unzip' - ps: Start-FileDownload "https://Github.com/hpi-swa/smalltalkCI/archive/master.zip" "C:\smalltalkCI.zip" - 7z x C:\smalltalkCI.zip -oC:\ -y > NULL build: false test_script: - '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0</dev/null; $SCI_RUN"' 68 7.3 Adding badges 7.3 Adding badges With CI happily running, you can add a badge to your README that will show the current status of your project. Here is the badge of the Containers-Stack project where we also enabled the coveralls.io test coverage service. # Containers-Stack A dead stupid stack implementation, but one fully working :) [![Build Status](https://travis-ci.com/Ducasse/Containers-Stack.svg?branch=master)] (https://travis-ci.com/Ducasse/Containers-Stack) [![Coverage Status](https://coveralls.io/repos/Github//Ducasse/Containers-Stack/badge.svg? (https://coveralls.io/Github//Ducasse/Containers-Stack?branch=master) [![License](https://img.shields.io/badge/license-MIT-blue.svg)]() [![Pharo version](https://img.shields.io/badge/Pharo-7.0-%23aac9ff.svg)] (https://pharo.org/download) [![Pharo version](https://img.shields.io/badge/Pharo-8.0-%23aac9ff.svg)] (https://pharo.org/download) ## Installation The following script installs Containers-Stack in Pharo. ```smalltalk Metacello new baseline: 'ContainersStack'; repository: 'Github://Ducasse/Containers-Stack/src'; load. ``` To obtain the necessary link, click on the badge in your Travis project overview and select one of the options. You can insert the markdown code directly into your README.md. 7.4 Conclusion With a continuous integration setup, you make sure that you can run your software on different platforms. In addition you make sure that you will be able to smoothly load your code and that any addition or modifications you just did is not disrupting the whole project. Other services such as coveralls are available for Pharo. 69 CHAPTER 8 Contributing to Pharo Pharo itself is hosted on GitHub under http://www.github.com/pharo-project/. All the development of the core system is done via this code repository. You can contribute to Pharo itself fixing some issues. The official and unique way of submitting code is by doing a Pull Request from your fork to the official repository. You can see the issues on the Pharo Github repository: http:// www.github.com/pharo-project/pharo/issues. 8.1 In a nutshell The general process is summarised by the following steps: 1. Download the latest version (this is important since I will make sure that you do not have to resync your fork). 2. Use repair (it will fetch some commits and make sure that your local repository is in sync the remotes). 3. Use repair (and create a branch let us called Bottom-123 that will point to the commits of your image). 4. Create a new branch for the issue you want to work on. 5. Code, then commit, then push to YOUR fork 6. Issue a PullRequest from your fork to Pharo 7. Check out your Bottom-123 branch and you can restart step 5 8.2 Step 0: Setting up the development environment Download and launch latest Pharo version: 71 Contributing to Pharo The recommended way to download Pharo is to use the Pharo launcher, which you can get from Pharo download’s page. Otherwise, if you have a nice command line environment (wget, readline, bash), download latest develop- ment pharo using zeroconf. Note Pro tip: If you’re on windows and you want a nice command line environment, install msys wget -O- get.pharo.org/64/90+vm | bash ./pharo-ui Pharo.image If you don’t have a command line environment, you can download both im- age and vm manually from the following links for windows for example: • Image: http://files.pharo.org/get-files/90/pharo.zip • VM: http://files.pharo.org/get-files/90/pharo-win-stable.zip Or you can browse the file server and find the files suiting your environment. 8.3 Fork the Pharo repository All changes you’ll do will be versionned in your own fork of the Pharo repos- itory. Then, from your fork you’ll be able to issue pull requests to Pharo, where they will be reviewed, and luckily, integrated. Go to Pharo github’s repository and click on the fork button on the top right. Yes, this means that you’ll need a github account to contribute to Pharo, yes. 8.4 Setup Iceberg To be able to contribute to Pharo, you need a Pharo git repository. You will need to set a valid set of credentials in your system to be able to work with Pharo. In case you use SSH (the default way), you will need to make sure those keys are available but you can start by using HTTPS since this is easier. In case they are not (and you will notice as soon as you try to clone a project or commit a change into one), you can add them following this steps: Linux setting credentials Execute in your shell: ssh-add ~/.ssh/id_rsa 72 8.5 Step 1. Setting up your repository OSX setting credentials Execute in your shell: ssh-add -K ~/.ssh/id_rsa Both for OSX and linux you can add such lines in your .bash_profile (or the one corresponding to your shell installation) so they are automatically executed on each new shell session. Windows setting credentials in windows is more complicated, you may need to generate a pair of keys (that needs to be uploaded to your account on github). You can follow in- structions on how to generate your keys http://guides.beanstalkapp.com/ version-control/git-on-windows.html#installing-ssh-keys. Pharo side settings Then you need to go to settings browser, search for ”Use custom SSH keys” and complete your data there. Alternatively, you can execute in your image playground: IceCredentialsProvider useCustomSsh: true. IceCredentialsProvider sshCredentials publicKey: 'path\to\ssh\id_rsa.pub'; privateKey: 'path\to\ssh\id_rsa' Note Pro Tip: this can be used too in case you have a non default key file, you just need to replace ”id_rsa” with your file name. 8.5 Step 1. Setting up your repository A fresh Pharo image comes with a pre-configured Pharo repository. Now this repository is in state ”Local Repository Missing”. You can develop with the image without problem now if you want to send some enhancements to Pharo you will not be able to do it. Indeed the message Local Repository Missing means that the image does not find a Pharo clone in your disk. Repairing local repository missing. To solve this situation, you need to rebind your repository with a clone on your disk. Indeed even if you can browse the system class code, the image does not know where its repository is. The repair menu item/button will propose you some solutions. 73 Contributing to Pharo Figure 8-1 A fresh Pharo image by default indicates that it does not find the clone of Pharo. Figure 8-2 Repairing the Pharo project. And clicking on the Repair repository menu will show you the repair view, showing an explanation of the current situation and some proposed solu- tions: • Locate this repository in your file system: if you have already cloned your pharo fork repository on your disk you can simply point to it. Pharo will synchronise it as explained in the next steps. • Clone again this repository: you can clone again your Pharo fork from github to your local disc. Again your fork may be desynchronised from the actual Pharo version but Iceberg will manage it too. So depending on the action you chose: you can then choose to search in your disk for an existing clone or to clone your forked Pharo repository. Solving fetch required. Once the repository is associated with its a clone, it will check the repository status and most probably you’ll find out that your repository is in Fetch re- quired state. Indeed ”Fetch required” means that your image was built from a commit that cannot be found in your repository. In other words, we need to update your repository, doing a fetch it will update your repository. 74 8.6 Step 2: Work on your image and push your change Figure 8-3 Solving the detached working copy situation. To solve again, launch the repair action again. Usually, if you already have all your remotes correctly configured, doing a fetch will put you in a Detached Working Copy state. Note Pro Tip: Instead of doing a simple fetch (second option of the re- pair) it is quite powerful to create a branch (first option), give it a name like sync, bottom... This branch is pointing to the point where your repos- itory and image are in sync and this is super useful to be able to checkout it later when you want to do multiple PRs on different and unrelated is- sues. This way you can go back to a sync state in one click and be super productive. Solving the Detached Working Copy. Detached Working Copy means that the image commit does not correspond with the repository commit (more details of it in the Glossary). At this point, we need to synchronize both to be able to work. Most of the times, the easier thing to do in this case is to just create a new branch as suggested in the Pro Tip before. If you plan to only fix on issue and you already know which issue you’ll be working on, you can create a branch using the ”New branch from issue” option. Otherwise, a nice alternative is to create a temporary branch like temp/synch that you can later on remove. 8.6 Step 2: Work on your image and push your change Pick up or create an issue and fix it. Once you have modifications in your image, it’s time to push those changes to your fork and make a pull request 75 Contributing to Pharo Figure 8-4 Checking your pull request. from your fork to the main Pharo repository. To do that, we enforce the fol- lowing process: • you create a local branch for the issue, • you issue a pull request of that issue to the main pharo development branch. Once you have done and commited all your work, you need to push it and create a pull request. Right click on the repository and go to the Github plu- gin menu item. Select the option Create pull request and select as target branch pharo’s development branch. And issue the pull request! Note Pro Tip: If your PR fixes the issue, you can put a keyword ”fixes #IssueNumber” in the description to automatically close the issue on merge. https://help.github.com/en/articles/closing-issues-using-keywords 8.7 Step 3: Follow your pull request If you go to github, you’ll see your pull request open and that some checks are running. Note that if your PR fails because of test failing or you want to integrate feedback provided by reviewed, it is super simple: just continue to commit in your branch. Your Pull Request will be updated with your new changes. This is always a cool feeling, so enjoy it. 8.8 Step 4: Once your pull request is integrated Some cleanups are required: • remove your branch from your fork • close the issue (this is done automatically if your PR contained ”fixes #numberOfIssue”) 76 8.9 Why you do not need to resync your fork with the pharo repo? 8.9 Why you do not need to resync your fork with the pharo repo? No, you do not need to explicitly resync your fork. Why? Because when you push your commits, you push also the Pharo commits. But let us look at it in details: • When you download an image (imagine that this image contains the code of commit3 and that Pharo’ repository is at commit5), the repair action will fetch all the commits (up to 5) and put them in your local repository. • When you create your bottom branch (the branch that points to the situation of your image when you downloaded) and later a child branch containing your fixes (call it myfix), it implies that when you commit your myfix branch, all the commits from myfixes and then from com- mit3 down to the common ancestor between your fork and your local repository will be pushed back to your fork. So if you downloaded the latest image (imagine for commit6 and that the pharo repository is at commit6 - yes this is the latest image!) when you push your branch (which here is child of commit6), automatically all the commits (6 down to the common ancestors between your local repo to your fork) will be pushed to your fork. So your fork will be in sync with Pharo’s one. So each time you work and push to your fork from the latest image, your fork gets updated! 8.10 Update your Pharo fork using the command line You can also update your Pharo fork using the command line in 6 commands. You need to have a git command line interface installed: $ git clone https://github.com/YOUR-USER-NAME/pharo.git $ cd Pharo $ git remote add pharo https://github.com/pharo-project/pharo.git $ git fetch pharo $ git pull pharo Pharo8.0 $ git push origin Pharo8.0 You can see the results in Figure 8-5. 8.11 Conclusion Now you know how to update a branch from your repository and can happily continue to improve Pharo. 77 Figure 8-5 Command Line. CHAPTER 9 Tips and Tricks This chapter contains some simple recipe-oriented frequently asked ques- tions. If you have some more please send them to us or even better, present a Pull Request. 9.1 How to use SSH keys In case SSH is not set up (and you will notice as soon as you try to clone a project or commit a change to one), there are some simple basic points to be able to use SSH with github. You can add SSH keys by following these steps (on Windows, if you want a nice command line environment, install http://mingw.org/wiki/msys): Generate a key pair To do this, execute the command: ssh-keygen -t rsa It will generate a private and a public key (on a unix-based installation in the directory .ssh). You should copy your id_rsa.pub key to your github account. Keep the keys in a safe place. On Windows, you can follow instructions on how to generate your keys at http://guides.beanstalkapp.com/version-control/git-on-windows.html#installing-ssh-keys. Add the key to your ssh agent In linux, execute in your shell: 79 Tips and Tricks ssh-add ~/.ssh/id_rsa In OSX, execute in your shell: ssh-add -K ~/.ssh/id_rsa For both OSX and linux you can add such lines to your .bash_profile (or the one corresponding to your shell installation such as .zshrc) so they are automatically executed on each new shell session. 9.2 How to contribute back to a project You cloned a project from a Github repository and you don’t have any write permissions. You made some changes, and you’d like to send a pull-request to it. What is the ”Iceberg way” of doing it? Manually you can fork the project on Github, add a remote to the local repo, push the changes to this new remote and then create the pull request on Github. With Iceberg you can do the same since Iceberg follows the git way: 1. Create fork on Github 2. Add a repository for this project to Iceberg (Repositories context menu then ”Repository” and then "+ Repository" 3. Create an issue branch with Iceberg (Repositories context menu then GitHub then Create new branch for issue). 4. Commit your changes with Iceberg. 5. Push your change to your fork from Iceberg. 6. Create pull request from Iceberg (Repositories context menu then GitHub then Create Pull Request) 9.3 How to distribute your changes in different branches With Iceberg we can chose the packages, classes or methods when we save. Imagine that you are in branch odd and you code m1, m2, m3, m4. Then later you realize too late that it would be great to extract m2 and m4 in another branch because you would like to merge it in another branch called even. To commit changes into different branches, you must change your branch. Because a commit always modifies the current branch. When you change a branch, there is first a preview. In the preview, you have a diff and you have a combo-box to select a checkout strategy: • The default checkout strategy is to reload the packages, this will over- ride any local changes you had. 80 9.3 How to distribute your changes in different branches Figure 9-1 Checkout choices. • The last one is to not load any packages: in other words, change the branch but do not touch my image. So one way to do save your changes in separate branch is to: 1. Commit in branch odd by selecting a subset of changes 2. Change to branch even using the ”Do not checkout” strategy 3. Commit in branch even a subset of changes 81 CHAPTER 10 Iceberg Glossary Git is complicated. Git with (Pharo) images is even more complicated. This page introduces the vocabulary used by Iceberg. Part of this vocabulary is Git vocabulary, part of it is Github’s vocabulary, part of it is introduced by Iceberg. 10.1 Git Disk Working Copy (Git) It is important not to confuse the code on your disk with the one of the repos- itory itself. The repository (a kind of database) has a lot more information, such as known branches, history of commits, remote repositories, the git in- dex and much more. Normally this information is kept in a directory named .git. The files that you see on your disk and that you edit are just a working copy of the contents in the repository. The git index (Git) The index is an intermediate structure which is used to select the contents that are going to be committed. So, to commit changes to your local repository, two actions are needed: 1. git add someFileOrDirectory will add someFileOrDirectory to the index. 2. git commit will create a new commit out of the contents of the index, which will be added to your local repository and to the current branch. 83 Iceberg Glossary When using Iceberg, you normally do not need to think about the index, Ice- berg will handle it for you. However, you might need to be aware that the index is part of the git repository, so if you have other tools working with the same repository there might be conflicts between them. Local and remote repositories (Git) To work with Git you always need a local repository (which is different from the code you see on your disk, that is not the repository, that is just your working copy). Remember that the local repository is a kind of a code database. Most frequently your local repository will be related with one remote reposi- tory which is called origin and will be the default target for pull and push. Upstream (Git) The upstream of a branch is a remote branch which is the default source when you pull and the default target when you push. Most probably it is a branch with the same name in your origin remote repository. Commit-ish (Git) A commit-ish is a reference that specifies a commit. Git command line tools usually accept several ways of specifying a commit, such as a branch or tag name, a SHA1 commit id, and several fatality-like combinations of symbols such as HEAD^, @{u} or master~2. The following table contains examples for each commit-ish expression. A complete description of the ways to specify a commit (and other git objects) can be found here1 . ---------------------------------------------------------------------- | Format | Examples ---------------------------------------------------------------------- | 1. <sha1> | dae86e1950b1277e545cee180551750029cfe735 | 2. <describeOutput> | v1.7.4.2-679-g3bee7fb | 3. <refname> | master, heads/master, refs/heads/master | 4. <refname>@{<date>} | master@{yesterday}, HEAD@{5 minutes ago} | 5. <refname>@{<n>} | master@{1} | 6. @{<n>} | @{1} | 7. @{-<n>} | @{-1} | 8. <refname>@{upstream} | master@{upstream}, @{u} | 9. <rev>^ | HEAD^, v1.5.1^0 | 10. <rev>~<n> | master~3 1 https://mirrors.edge.kernel.org/pub/software/scm/git/docs/gitrevisions.html#_specifying_ revisions 84 10.2 Iceberg | 11. <rev>^{<type>} | v0.99.8^{commit} | 12. <rev>^{} | v0.99.8^{} | 13. <rev>^{/<text>} | HEAD^{/fix nasty bug} | 14. :/<text> | :/fix nasty bug ---------------------------------------------------------------------- 10.2 Iceberg Iceberg Working Copy (Iceberg) Iceberg also includes an object called the working copy that is not quite the same as Git’s working copy. Iceberg’s working copy represents the code loaded in the Pharo image, with the loaded commit and the packages. Local Repository Missing (Iceberg) The Local Repository Missing status is shown by iceberg when a project in the image does not find its repository on disk. This happens most probably because you have downloaded an image that somebody else created, or you deleted/moved a git repository in your disk. Most of the times this status is not shown because iceberg automatically manages disk repositories. To recover from this status, you need to update your repository by cloning a new git repository or by configuring an existing repository on disk. Fetch required. Unknown ... (Iceberg) The Fetch required status is shown by Iceberg when a project in the image was loaded from a commit that cannot be found in its local repository. This happens most probably because you’ve downloaded an image that somebody else created, and/or your repository on disk is not up to date. To recover from this status, you need to fetch from remotes to try to find the missing commit. It may happen that the missing commit is not in one of your configured remotes (even that nobody ever pushed it). In that case, the easiest solution is to discard your image changes and checkout an existing branch/commit. Detached Working Copy (Iceberg) The Detached working Copy status is shown by Iceberg when a project in the image was loaded from a commit does not correspond with the current commit on disk. This happens most probably because you’ve modified your repository from the command line. To recover from this status, you need to align your repository with your working copy. Either you can 85 Iceberg Glossary 1. discard your image changes and load the repository commit, 2. checkout a new branch pointing to your working copy commit or 3. merge what is in the image into the current branch. Detached HEAD (Git) The Detached HEAD status means that the current repository on disk is not working on a branch but on a commit. From a git stand-point you can com- mit and continue working but your changes may get lost as the commit is not pointed to by any branch. From an Iceberg stand-point, we forbid commit in this state to avoid difficult to understand and repair situations. To recover from this status, you need to checkout a (new or existing) branch. 86 Bibliography 87