stephen.news

hypertext, words and more

Git

  • I use git log a lot. It’s real handy. But you know what — it’s pretty unwieldy and takes up quite a bit of terminal real estate. I finally decided to do something about it. Every time I ran git log, my terminal becomes a total mess. I knew about this little dingle:

    git log --pretty=oneline --abbrev-commit

    Which prints out a friendly, easy-to-digest, single-lined log:

    a2a722b (HEAD -> master, origin/master) Fix the NoteDetail styles.
    4fdcbc7 Split the NoteCell and NoteDetail.
    ccc4dc2 Connected the NoteCell with data.
    bce0d12 Broken components into separate pieces.
    071ea13 Change destinations, now the targeting works.
    0ecea01 NoteDetail and split it from the RootView.
    ...

    Which is great! But… can we do better? Some googling around lead me to this post by Mattias Geniar:

    There are longer versions of that same --pretty parameter. In fact, it allows you to specify all the fields you want in the output.

    Sweet! So, running the same command and flagging the specified fields like so, prints a more concise log. Awesome!

    $ git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
    
    a2a722b (HEAD -> master, origin/master) Fix the NoteDetail styles. (2 days ago) <Stephen Petrey>
    4fdcbc7 Split the NoteCell and NoteDetail. (2 days ago) <Stephen Petrey>
    ccc4dc2 Connected the NoteCell with data. (2 days ago) <Stephen Petrey>
    bce0d12 Broken components into separate pieces. (4 days ago) <Stephen Petrey>
    071ea13 Change destinations, now the targeting works. (5 days ago) <Stephen Petrey>
    0ecea01 NoteDetail and split it from the RootView. (6 days ago) <Stephen Petrey>
    ...

    Mattias has the excellent suggestion of configuring a Git Alias for this:

    $ git config --global alias.logline "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

    Now, instead of having to type out the command and flag the fields you want to print, you can just type, git logline and wham! It should print out a prettified git log that takes up way less space in your terminal.

  • This took some googling, so I thought I would document my findings here.

    Locally

    If you’re already on the branch you’d like to rename, just run:

    git branch -m new-name

    If you’re on say master, and you have a branch you want to rename now but no checkout, run:

    git branch -m old-name new-name

    Now that you’ve taken care of your local branch, there’s the unfinished business of mending your remote branch with renaming.

    Remote

    If you use Github or Gitlab (or whatever) you’re going to want to remove the old-name branch and push the new-name branch:

    git push origin :old-name new-name

    And finally, push to reset the upstream branch so your local and remote branches are up-to-date:

    git push origin -u new-name
  • So here’s a new one for ya.

    I was working on a branch all day, closed the laptop, went home and fell asleep. Nothing out of the ordinary. The next day, I awoke, and upon returning to work, immediately began working on a new problem.

    The issue arose when I discovered to my horror that I was still on the same branch from yesterday! *Gasp*

    Lest we forget, we have Git at our disposal — so I take a breath and dive in:

    git status

    This will probably return a list of changes not staged like this:

    On branch fix-from-yesterday
    Your branch is up to date with 'origin/fix-from-yesterday-'.

    Changes not staged for commit:
    (use "git add …" to update what will be committed)
    (use "git checkout -- …" to discard changes in working directory)
    modified: project/api-v4.php
    modified: web/assets/js/some-project/package.json
    ...

    Now that we have a pulse on things, we can do this:

    git checkout -b new-branch-of-changes-for-today

    This will leave your current branch as is, create and checkout a new branch and keep all your changes. You can then make a commit with:

    git add <the files you want commit>

    and finally, commit to your new branch with ol trusty:

    git commit -m "Make sure your description is short and sweet"

    And there you have it! According to the git-checkout documentation, -b and -B are interchangeable. Here’s the rub:

    Specifying -b causes a new branch to be created as if git-branch[1] were called and then checked out. In this case you can use the --track or --no-track options, which will be passed to git branch. As a convenience, --track without -b implies branch creation; see the description of --trackbelow.


    If -B is given, <new_branch> is created if it doesn’t exist; otherwise, it is reset. This is the transactional equivalent of

  • Alright, we’ve all been there. You got some merge conflicts. Or there’s a last minute hotfix. You feverishly make a commit. Then, your worst fear is realized only after you’ve pushed it remotely.

    You made a mistake. The horror.

    GIF by Justin - Find & Share on GIPHY

    First, don’t sweat it. You have options. Most of the time, when I do this, I just need to remove the commit altogether. Other times, I need to make revisions, and replace that commit altogether. Tabula rasa amirite?

    Okay so let’s get started. First, I’m making a few assumptions. I’m doing work on a branch separate from master. Let’s say you’re working on Navigation, I would be on a branch called navigation or nav-component or something. It’s good practice to keep your work separate, and branched.

    Alrighty, with that out of the way, let’s run git log on our example navigation branch:

    $ git log --pretty=oneline --abbrev-commit
    32cd83e Made a change with a mistake
    b9u3cc5 Changed again
    105fd3d Updated some content
    5cd7718 Fixed the NavBar
    a1842d4 Initial commit

    Alright so looking over our log here, we can see at the top, that commit 32cd83e is the most recent commit. It’s the problematic commit in question. To edit out commit history we are going to use rebase:

    $ git rebase -i HEAD~2

    Before you run this, let’s break down what we’re doing here. A classic use-case of rebase is when you’re on a branch say, navigation. But, let’s say that it’s been a week since you branched. It’s likely that master has diverged since you created your branch. What divergence means, is that the branch (we’re on) navigation is currently out-of-date and lacks the new history (from the past week) on master. We don’t have to worry about that here per se.

    But, when you work in repos that multiple people contribute to, a branch can be out-of-date very often. If you use master as the center of truth, the organization can use rebase to bring other contributors history into your branch, and therefor, up-to-date with master. Neat huh?

    Anyways, you could say what we’re doing here, is similar. Instead of using rebase to replay work from a branch on top of master, we want to rewrite the commit history of the branch we’re currently on. After running the command above, you should see something like this:

    b9u3cc5 Changed again
    32cd83e Made a change with a mistake
    
    # Rebase 105fd3d..46cd867 onto 105fd3d
    #
    # Commands:
    #  p, pick = use commit
    #  r, reword = use commit, but edit the commit message
    #  e, edit = use commit, but stop for amending
    #  s, squash = use commit, but meld into previous commit
    #  f, fixup = like "squash", but discard this commit's log message
    #  x, exec = run command (the rest of the line) using shell
    #
    # If you remove a line here THAT COMMIT WILL BE LOST.
    # However, if you remove everything, the rebase will be aborted.
    #

    Here, (and admittedly confusingly) the most recent commit, 32cd83e we want to destroy is listed at the bottom instead of the top. You can see pretty clearly what the HEAD~2 argument does. It flags rebase to only show the most recent 2 commits in this interactive rebase wizard. Because, that’s all we need to see for context.

    So, the options we have now is we can remove that line altogether, and the commit will be dropped into oblivion, as if it never happened. If we remove the problematic commit, and exit the rebase wizard, we can now check our history on navigation:

    $ git log --pretty=oneline --abbrev-commit
    b9u3cc5 Changed again
    105fd3d Updated some content
    5cd7718 Fixed the NavBar
    a1842d4 Initial commit

    Look ma! No mistake! 32cd83e is gone! Now that we have this history locally, let’s push it up, remotely:

    $ git push origin +navigation

    The + flag before the branch name signals that this will be a force push. Alternatively you could have written the same command as:

    $ git push -f origin navigation

    I like using this technique, because I enjoy committing everything. Mistakes and all. Because, well for one, why not? Documenting your code history can be helpful sometimes. Besides, ultimately, you can use rebase to squash and fixup your commit history anyways. So, when it comes time to push to remote, I just take a beat and git rebase -i HEAD~N to keep my history lean. I’ve only recently learned the pains of not keeping my git history pretty and concise. So, now I’m a convert, and everyone benefits from a readable git history.

    Further Reading:

  • As part of my ongoing effort to become more effective at Git, I stumbled into an interesting problem. At Vimeo, we have a lot of branches named after issues reported in Jira. Some branches take on the name of the project or component or a very specific fix. We also have branches that don’t follow naming-conventions at all. It happens. Anyways, somedays, I forget the name of the branches I’ve worked on, say yesterday. Other times, I forgot what work I did on which branch. I grew tired of tab-switching between my terminal and Jira or Github to go and find my work history. I don’t need to see my commits, I just need to see a list of recent branches.

    So, I began googling around the web, and came across this post. It’s a good solution I think, David Walsh seems to agree too. Basically, it lists the most recent branches you’ve worked on. But ideally, I’d rather opt for an alias in my .bashrc. I don’t want to clutter up my .gitconfig. That’s, just like my opinion man. Without further ado, here’s my alias in my .bashrc:

    # ~/.bashrc
    
    alias recent-branches="git for-each-ref --sort=-committerdate --count=10 --format='%(refname:short)' refs/heads/"

    Stupid simple, readable and short. Just the way I like it. There are of course, more complex solutions to this simple problem. But I’m not a huge fan of those longwinded solutions. Anyways, when I run the recent-branches alias in my terminal:

    ➜  example-repo git:(feature-pages) recent-branches
    nav-bar-component-fix
    GH-48
    features-pages
    FR-47
    XR-73
    VP-71
    new-privacy-policy-page
    VP-70
    VP-49
    GH-46

    Nifty huh? As you can see, the alias command is really just one long git command. It takes a count=10 flag. So if you need to see more branches in your history, just inflate the value accordingly. Enjoy! ✌️